mirror of
https://github.com/apache/superset.git
synced 2026-04-19 16:14:52 +00:00
refactor(monorepo): move superset-ui to superset(stage 2) (#17552)
This commit is contained in:
@@ -0,0 +1,72 @@
|
||||
/**
|
||||
* 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-heatmap {
|
||||
position: relative;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.superset-legacy-chart-heatmap .axis text {
|
||||
font: 10px sans-serif;
|
||||
text-rendering: optimizeLegibility;
|
||||
fill: #555;
|
||||
}
|
||||
|
||||
.superset-legacy-chart-heatmap .background-rect {
|
||||
stroke: #ddd;
|
||||
fill-opacity: 0;
|
||||
pointer-events: all;
|
||||
}
|
||||
|
||||
.superset-legacy-chart-heatmap .axis path,
|
||||
.superset-legacy-chart-heatmap .axis line {
|
||||
fill: none;
|
||||
stroke: #ddd;
|
||||
shape-rendering: crispEdges;
|
||||
}
|
||||
|
||||
.superset-legacy-chart-heatmap canvas,
|
||||
.superset-legacy-chart-heatmap img {
|
||||
image-rendering: optimizeSpeed; /* Older versions of FF */
|
||||
image-rendering: -moz-crisp-edges; /* FF 6.0+ */
|
||||
image-rendering: -webkit-optimize-contrast; /* Safari */
|
||||
image-rendering: -o-crisp-edges; /* OS X & Windows Opera (12.02+) */
|
||||
image-rendering: pixelated; /* Awesome future-browsers */
|
||||
-ms-interpolation-mode: nearest-neighbor; /* IE */
|
||||
}
|
||||
|
||||
.superset-legacy-chart-heatmap .legendCells text {
|
||||
font-size: 10px;
|
||||
font-weight: normal;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.superset-legacy-chart-heatmap .legendCells .cell:first-child text {
|
||||
opacity: 1;
|
||||
}
|
||||
.superset-legacy-chart-heatmap .legendCells .cell:last-child text {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.dashboard .superset-legacy-chart-heatmap .axis text {
|
||||
font-size: 10px;
|
||||
opacity: 0.75;
|
||||
}
|
||||
@@ -0,0 +1,455 @@
|
||||
/**
|
||||
* 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 func-names, react/sort-prop-types */
|
||||
import d3 from 'd3';
|
||||
import PropTypes from 'prop-types';
|
||||
import 'd3-svg-legend';
|
||||
import d3tip from 'd3-tip';
|
||||
import {
|
||||
getNumberFormatter,
|
||||
NumberFormats,
|
||||
getSequentialSchemeRegistry,
|
||||
} from '@superset-ui/core';
|
||||
|
||||
import './vendor/d3tip.css';
|
||||
import './Heatmap.css';
|
||||
|
||||
const propTypes = {
|
||||
data: PropTypes.shape({
|
||||
records: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
x: PropTypes.string,
|
||||
y: PropTypes.string,
|
||||
v: PropTypes.number,
|
||||
perc: PropTypes.number,
|
||||
rank: PropTypes.number,
|
||||
}),
|
||||
),
|
||||
extents: PropTypes.arrayOf(PropTypes.number),
|
||||
}),
|
||||
width: PropTypes.number,
|
||||
height: PropTypes.number,
|
||||
bottomMargin: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
colorScheme: PropTypes.string,
|
||||
columnX: PropTypes.string,
|
||||
columnY: PropTypes.string,
|
||||
leftMargin: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
|
||||
metric: PropTypes.oneOfType([PropTypes.string, PropTypes.object]),
|
||||
normalized: PropTypes.bool,
|
||||
numberFormat: PropTypes.string,
|
||||
showLegend: PropTypes.bool,
|
||||
showPercentage: PropTypes.bool,
|
||||
showValues: PropTypes.bool,
|
||||
sortXAxis: PropTypes.string,
|
||||
sortYAxis: PropTypes.string,
|
||||
xScaleInterval: PropTypes.number,
|
||||
yScaleInterval: PropTypes.number,
|
||||
yAxisBounds: PropTypes.arrayOf(PropTypes.number),
|
||||
};
|
||||
|
||||
function cmp(a, b) {
|
||||
return a > b ? 1 : -1;
|
||||
}
|
||||
|
||||
const DEFAULT_PROPERTIES = {
|
||||
minChartWidth: 150,
|
||||
minChartHeight: 150,
|
||||
marginLeft: 35,
|
||||
marginBottom: 35,
|
||||
marginTop: 10,
|
||||
marginRight: 10,
|
||||
};
|
||||
|
||||
// Inspired from http://bl.ocks.org/mbostock/3074470
|
||||
// https://jsfiddle.net/cyril123/h0reyumq/
|
||||
function Heatmap(element, props) {
|
||||
const {
|
||||
data,
|
||||
width,
|
||||
height,
|
||||
bottomMargin,
|
||||
canvasImageRendering,
|
||||
colorScheme,
|
||||
columnX,
|
||||
columnY,
|
||||
leftMargin,
|
||||
metric,
|
||||
normalized,
|
||||
numberFormat,
|
||||
showLegend,
|
||||
showPercentage,
|
||||
showValues,
|
||||
sortXAxis,
|
||||
sortYAxis,
|
||||
xScaleInterval,
|
||||
yScaleInterval,
|
||||
yAxisBounds,
|
||||
} = props;
|
||||
|
||||
const { records, extents } = data;
|
||||
|
||||
const margin = {
|
||||
top: 10,
|
||||
right: 10,
|
||||
bottom: 35,
|
||||
left: 35,
|
||||
};
|
||||
|
||||
let showY = true;
|
||||
let showX = true;
|
||||
const pixelsPerCharX = 4.5; // approx, depends on font size
|
||||
const pixelsPerCharY = 6; // approx, depends on font size
|
||||
|
||||
const valueFormatter = getNumberFormatter(numberFormat);
|
||||
|
||||
// Dynamically adjusts based on max x / y category lengths
|
||||
function adjustMargins() {
|
||||
let longestX = 1;
|
||||
let longestY = 1;
|
||||
|
||||
records.forEach(datum => {
|
||||
longestX = Math.max(
|
||||
longestX,
|
||||
(datum.x && datum.x.toString().length) || 1,
|
||||
);
|
||||
longestY = Math.max(
|
||||
longestY,
|
||||
(datum.y && datum.y.toString().length) || 1,
|
||||
);
|
||||
});
|
||||
|
||||
if (leftMargin === 'auto') {
|
||||
margin.left = Math.ceil(Math.max(margin.left, pixelsPerCharY * longestY));
|
||||
} else {
|
||||
margin.left = leftMargin;
|
||||
}
|
||||
|
||||
if (showLegend) {
|
||||
margin.right += 40;
|
||||
}
|
||||
|
||||
margin.bottom =
|
||||
bottomMargin === 'auto'
|
||||
? Math.ceil(Math.max(margin.bottom, pixelsPerCharX * longestX))
|
||||
: bottomMargin;
|
||||
}
|
||||
|
||||
// Check if x axis "x" position is outside of the container and rotate labels 90deg
|
||||
function checkLabelPosition(container) {
|
||||
const xAxisNode = container.select('.x.axis').node();
|
||||
|
||||
if (!xAxisNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
xAxisNode.getBoundingClientRect().x + 4 <
|
||||
container.node().getBoundingClientRect().x
|
||||
) {
|
||||
container
|
||||
.selectAll('.x.axis')
|
||||
.selectAll('text')
|
||||
.attr('transform', 'rotate(-90)')
|
||||
.attr('x', -6)
|
||||
.attr('y', 0)
|
||||
.attr('dy', '0.3em');
|
||||
}
|
||||
}
|
||||
|
||||
function ordScale(k, rangeBands, sortMethod) {
|
||||
let domain = {};
|
||||
const actualKeys = {}; // hack to preserve type of keys when number
|
||||
records.forEach(d => {
|
||||
domain[d[k]] = (domain[d[k]] || 0) + d.v;
|
||||
actualKeys[d[k]] = d[k];
|
||||
});
|
||||
// Not usgin object.keys() as it converts to strings
|
||||
const keys = Object.keys(actualKeys).map(s => actualKeys[s]);
|
||||
if (sortMethod === 'alpha_asc') {
|
||||
domain = keys.sort(cmp);
|
||||
} else if (sortMethod === 'alpha_desc') {
|
||||
domain = keys.sort(cmp).reverse();
|
||||
} else if (sortMethod === 'value_desc') {
|
||||
domain = Object.keys(domain).sort((a, b) =>
|
||||
domain[a] > domain[b] ? -1 : 1,
|
||||
);
|
||||
} else if (sortMethod === 'value_asc') {
|
||||
domain = Object.keys(domain).sort((a, b) =>
|
||||
domain[b] > domain[a] ? -1 : 1,
|
||||
);
|
||||
}
|
||||
|
||||
if (k === 'y' && rangeBands) {
|
||||
domain.reverse();
|
||||
}
|
||||
|
||||
if (rangeBands) {
|
||||
return d3.scale.ordinal().domain(domain).rangeBands(rangeBands);
|
||||
}
|
||||
|
||||
return d3.scale.ordinal().domain(domain).range(d3.range(domain.length));
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-param-reassign
|
||||
element.innerHTML = '';
|
||||
const matrix = {};
|
||||
|
||||
adjustMargins();
|
||||
|
||||
let hmWidth = width - (margin.left + margin.right);
|
||||
let hmHeight = height - (margin.bottom + margin.top);
|
||||
const hideYLabel = () => {
|
||||
margin.left =
|
||||
leftMargin === 'auto' ? DEFAULT_PROPERTIES.marginLeft : leftMargin;
|
||||
hmWidth = width - (margin.left + margin.right);
|
||||
showY = false;
|
||||
};
|
||||
|
||||
const hideXLabel = () => {
|
||||
margin.bottom =
|
||||
bottomMargin === 'auto' ? DEFAULT_PROPERTIES.marginBottom : bottomMargin;
|
||||
hmHeight = height - (margin.bottom + margin.top);
|
||||
showX = false;
|
||||
};
|
||||
|
||||
// Hide Y Labels
|
||||
if (hmWidth < DEFAULT_PROPERTIES.minChartWidth) {
|
||||
hideYLabel();
|
||||
}
|
||||
|
||||
// Hide X Labels
|
||||
if (
|
||||
hmHeight < DEFAULT_PROPERTIES.minChartHeight ||
|
||||
hmWidth < DEFAULT_PROPERTIES.minChartWidth
|
||||
) {
|
||||
hideXLabel();
|
||||
}
|
||||
|
||||
if (showY && hmHeight < DEFAULT_PROPERTIES.minChartHeight) {
|
||||
hideYLabel();
|
||||
}
|
||||
|
||||
const fp = getNumberFormatter(NumberFormats.PERCENT);
|
||||
|
||||
const xScale = ordScale('x', null, sortXAxis);
|
||||
const yScale = ordScale('y', null, sortYAxis);
|
||||
const xRbScale = ordScale('x', [0, hmWidth], sortXAxis);
|
||||
const yRbScale = ordScale('y', [hmHeight, 0], sortYAxis);
|
||||
const X = 0;
|
||||
const Y = 1;
|
||||
const heatmapDim = [xRbScale.domain().length, yRbScale.domain().length];
|
||||
|
||||
const minBound = yAxisBounds[0] || 0;
|
||||
const maxBound = yAxisBounds[1] || 1;
|
||||
const colorScale = getSequentialSchemeRegistry()
|
||||
.get(colorScheme)
|
||||
.createLinearScale([minBound, maxBound]);
|
||||
|
||||
const scale = [
|
||||
d3.scale.linear().domain([0, heatmapDim[X]]).range([0, hmWidth]),
|
||||
d3.scale.linear().domain([0, heatmapDim[Y]]).range([0, hmHeight]),
|
||||
];
|
||||
|
||||
const container = d3.select(element);
|
||||
container.classed('superset-legacy-chart-heatmap', true);
|
||||
|
||||
const canvas = container
|
||||
.append('canvas')
|
||||
.attr('width', heatmapDim[X])
|
||||
.attr('height', heatmapDim[Y])
|
||||
.style('width', `${hmWidth}px`)
|
||||
.style('height', `${hmHeight}px`)
|
||||
.style('image-rendering', canvasImageRendering)
|
||||
.style('left', `${margin.left}px`)
|
||||
.style('top', `${margin.top}px`)
|
||||
.style('position', 'absolute');
|
||||
|
||||
const svg = container
|
||||
.append('svg')
|
||||
.attr('width', width)
|
||||
.attr('height', height)
|
||||
.attr('class', 'heatmap-container')
|
||||
.style('position', 'relative');
|
||||
|
||||
if (showValues) {
|
||||
const cells = svg
|
||||
.selectAll('rect')
|
||||
.data(records)
|
||||
.enter()
|
||||
.append('g')
|
||||
.attr('transform', `translate(${margin.left}, ${margin.top})`);
|
||||
|
||||
cells
|
||||
.append('text')
|
||||
.attr('transform', d => `translate(${xRbScale(d.x)}, ${yRbScale(d.y)})`)
|
||||
.attr('y', yRbScale.rangeBand() / 2)
|
||||
.attr('x', xRbScale.rangeBand() / 2)
|
||||
.attr('text-anchor', 'middle')
|
||||
.attr('dy', '.35em')
|
||||
.text(d => valueFormatter(d.v))
|
||||
.attr(
|
||||
'font-size',
|
||||
`${Math.min(yRbScale.rangeBand(), xRbScale.rangeBand()) / 3}px`,
|
||||
)
|
||||
.attr('fill', d => (d.v >= extents[1] / 2 ? 'white' : 'black'));
|
||||
}
|
||||
|
||||
if (showLegend) {
|
||||
const colorLegend = d3.legend
|
||||
.color()
|
||||
.labelFormat(valueFormatter)
|
||||
.scale(colorScale)
|
||||
.shapePadding(0)
|
||||
.cells(10)
|
||||
.shapeWidth(10)
|
||||
.shapeHeight(10)
|
||||
.labelOffset(3);
|
||||
|
||||
svg
|
||||
.append('g')
|
||||
.attr('transform', `translate(${width - 40}, ${margin.top})`)
|
||||
.call(colorLegend);
|
||||
}
|
||||
|
||||
const tip = d3tip()
|
||||
.attr('class', 'd3-tip')
|
||||
.offset(function () {
|
||||
const k = d3.mouse(this);
|
||||
const x = k[0] - hmWidth / 2;
|
||||
|
||||
return [k[1] - 20, x];
|
||||
})
|
||||
.html(function () {
|
||||
let s = '';
|
||||
const k = d3.mouse(this);
|
||||
const m = Math.floor(scale[0].invert(k[0]));
|
||||
const n = Math.floor(scale[1].invert(k[1]));
|
||||
const metricLabel = typeof metric === 'object' ? metric.label : metric;
|
||||
if (m in matrix && n in matrix[m]) {
|
||||
const obj = matrix[m][n];
|
||||
s += `<div><b>${columnX}: </b>${obj.x}<div>`;
|
||||
s += `<div><b>${columnY}: </b>${obj.y}<div>`;
|
||||
s += `<div><b>${metricLabel}: </b>${valueFormatter(obj.v)}<div>`;
|
||||
if (showPercentage) {
|
||||
s += `<div><b>%: </b>${fp(normalized ? obj.rank : obj.perc)}<div>`;
|
||||
}
|
||||
tip.style('display', null);
|
||||
} else {
|
||||
// this is a hack to hide the tooltip because we have map it to a single <rect>
|
||||
// d3-tip toggles opacity and calling hide here is undone by the lib after this call
|
||||
tip.style('display', 'none');
|
||||
}
|
||||
|
||||
return s;
|
||||
});
|
||||
|
||||
const rect = svg
|
||||
.append('g')
|
||||
.attr('transform', `translate(${margin.left}, ${margin.top})`)
|
||||
.append('rect')
|
||||
.classed('background-rect', true)
|
||||
.on('mousemove', tip.show)
|
||||
.on('mouseout', tip.hide)
|
||||
.attr('width', hmWidth)
|
||||
.attr('height', hmHeight);
|
||||
|
||||
rect.call(tip);
|
||||
|
||||
if (showX) {
|
||||
const xAxis = d3.svg
|
||||
.axis()
|
||||
.scale(xRbScale)
|
||||
.outerTickSize(0)
|
||||
.tickValues(xRbScale.domain().filter((d, i) => !(i % xScaleInterval)))
|
||||
.orient('bottom');
|
||||
|
||||
svg
|
||||
.append('g')
|
||||
.attr('class', 'x axis')
|
||||
.attr('transform', `translate(${margin.left},${margin.top + hmHeight})`)
|
||||
.call(xAxis)
|
||||
.selectAll('text')
|
||||
.attr('x', -4)
|
||||
.attr('y', 10)
|
||||
.attr('dy', '0.3em')
|
||||
.style('text-anchor', 'end')
|
||||
.attr('transform', 'rotate(-45)');
|
||||
}
|
||||
|
||||
if (showY) {
|
||||
const yAxis = d3.svg
|
||||
.axis()
|
||||
.scale(yRbScale)
|
||||
.outerTickSize(0)
|
||||
.tickValues(yRbScale.domain().filter((d, i) => !(i % yScaleInterval)))
|
||||
.orient('left');
|
||||
|
||||
svg
|
||||
.append('g')
|
||||
.attr('class', 'y axis')
|
||||
.attr('transform', `translate(${margin.left},${margin.top})`)
|
||||
.call(yAxis);
|
||||
}
|
||||
|
||||
checkLabelPosition(container);
|
||||
const context = canvas.node().getContext('2d');
|
||||
context.imageSmoothingEnabled = false;
|
||||
|
||||
// Compute the pixel colors; scaled by CSS.
|
||||
function createImageObj() {
|
||||
const imageObj = new Image();
|
||||
const image = context.createImageData(heatmapDim[0], heatmapDim[1]);
|
||||
const pixs = {};
|
||||
records.forEach(d => {
|
||||
const c = d3.rgb(colorScale(normalized ? d.rank : d.perc));
|
||||
const x = xScale(d.x);
|
||||
const y = yScale(d.y);
|
||||
pixs[x + y * xScale.domain().length] = c;
|
||||
if (matrix[x] === undefined) {
|
||||
matrix[x] = {};
|
||||
}
|
||||
if (matrix[x][y] === undefined) {
|
||||
matrix[x][y] = d;
|
||||
}
|
||||
});
|
||||
|
||||
let p = 0;
|
||||
for (let i = 0; i < heatmapDim[0] * heatmapDim[1]; i += 1) {
|
||||
let c = pixs[i];
|
||||
let alpha = 255;
|
||||
if (c === undefined) {
|
||||
c = d3.rgb('#F00');
|
||||
alpha = 0;
|
||||
}
|
||||
image.data[p + 0] = c.r;
|
||||
image.data[p + 1] = c.g;
|
||||
image.data[p + 2] = c.b;
|
||||
image.data[p + 3] = alpha;
|
||||
p += 4;
|
||||
}
|
||||
context.putImageData(image, 0, 0);
|
||||
imageObj.src = canvas.node().toDataURL();
|
||||
}
|
||||
createImageObj();
|
||||
}
|
||||
|
||||
Heatmap.displayName = 'Heatmap';
|
||||
Heatmap.propTypes = propTypes;
|
||||
|
||||
export default Heatmap;
|
||||
@@ -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/core';
|
||||
import Component from './Heatmap';
|
||||
|
||||
export default reactify(Component);
|
||||
@@ -0,0 +1,334 @@
|
||||
/**
|
||||
* 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 {
|
||||
FeatureFlag,
|
||||
isFeatureEnabled,
|
||||
t,
|
||||
validateNonEmpty,
|
||||
} from '@superset-ui/core';
|
||||
import {
|
||||
columnChoices,
|
||||
ControlPanelConfig,
|
||||
ControlPanelState,
|
||||
formatSelectOptions,
|
||||
formatSelectOptionsForRange,
|
||||
sections,
|
||||
dndEntity,
|
||||
} from '@superset-ui/chart-controls';
|
||||
|
||||
const sortAxisChoices = [
|
||||
['alpha_asc', t('Axis ascending')],
|
||||
['alpha_desc', t('Axis descending')],
|
||||
['value_asc', t('Metric ascending')],
|
||||
['value_desc', t('Metric descending')],
|
||||
];
|
||||
|
||||
const allColumns = {
|
||||
type: 'SelectControl',
|
||||
default: null,
|
||||
description: t('Columns to display'),
|
||||
mapStateToProps: (state: ControlPanelState) => ({
|
||||
choices: columnChoices(state.datasource),
|
||||
}),
|
||||
validators: [validateNonEmpty],
|
||||
};
|
||||
|
||||
const dndAllColumns = {
|
||||
...dndEntity,
|
||||
description: t('Columns to display'),
|
||||
};
|
||||
|
||||
const columnsConfig = isFeatureEnabled(FeatureFlag.ENABLE_EXPLORE_DRAG_AND_DROP)
|
||||
? dndAllColumns
|
||||
: allColumns;
|
||||
|
||||
const config: ControlPanelConfig = {
|
||||
controlPanelSections: [
|
||||
sections.legacyRegularTime,
|
||||
{
|
||||
label: t('Query'),
|
||||
expanded: true,
|
||||
controlSetRows: [
|
||||
[
|
||||
{
|
||||
name: 'all_columns_x',
|
||||
config: {
|
||||
...columnsConfig,
|
||||
label: 'X Axis',
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'all_columns_y',
|
||||
config: {
|
||||
...columnsConfig,
|
||||
label: 'Y Axis',
|
||||
},
|
||||
},
|
||||
],
|
||||
['metric'],
|
||||
['adhoc_filters'],
|
||||
['row_limit'],
|
||||
[
|
||||
{
|
||||
name: 'sort_by_metric',
|
||||
config: {
|
||||
type: 'CheckboxControl',
|
||||
label: t('Sort by metric'),
|
||||
description: t(
|
||||
'Whether to sort results by the selected metric in descending order.',
|
||||
),
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
{
|
||||
label: t('Heatmap Options'),
|
||||
expanded: true,
|
||||
tabOverride: 'customize',
|
||||
controlSetRows: [
|
||||
['linear_color_scheme'],
|
||||
[
|
||||
{
|
||||
name: 'xscale_interval',
|
||||
config: {
|
||||
type: 'SelectControl',
|
||||
label: t('XScale Interval'),
|
||||
renderTrigger: true,
|
||||
choices: formatSelectOptionsForRange(1, 50),
|
||||
default: '1',
|
||||
clearable: false,
|
||||
description: t(
|
||||
'Number of steps to take between ticks when displaying the X scale',
|
||||
),
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'yscale_interval',
|
||||
config: {
|
||||
type: 'SelectControl',
|
||||
label: t('YScale Interval'),
|
||||
choices: formatSelectOptionsForRange(1, 50),
|
||||
default: '1',
|
||||
clearable: false,
|
||||
renderTrigger: true,
|
||||
description: t(
|
||||
'Number of steps to take between ticks when displaying the Y scale',
|
||||
),
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'canvas_image_rendering',
|
||||
config: {
|
||||
type: 'SelectControl',
|
||||
label: t('Rendering'),
|
||||
renderTrigger: true,
|
||||
choices: [
|
||||
['pixelated', 'pixelated (Sharp)'],
|
||||
['auto', 'auto (Smooth)'],
|
||||
],
|
||||
default: 'pixelated',
|
||||
description: t(
|
||||
'image-rendering CSS attribute of the canvas object that ' +
|
||||
'defines how the browser scales up the image',
|
||||
),
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'normalize_across',
|
||||
config: {
|
||||
type: 'SelectControl',
|
||||
label: t('Normalize Across'),
|
||||
choices: [
|
||||
['heatmap', 'heatmap'],
|
||||
['x', 'x'],
|
||||
['y', 'y'],
|
||||
],
|
||||
default: 'heatmap',
|
||||
description: t(
|
||||
'Color will be rendered based on a ratio ' +
|
||||
'of the cell against the sum of across this ' +
|
||||
'criteria',
|
||||
),
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'left_margin',
|
||||
config: {
|
||||
type: 'SelectControl',
|
||||
freeForm: true,
|
||||
clearable: false,
|
||||
label: t('Left Margin'),
|
||||
choices: formatSelectOptions([
|
||||
'auto',
|
||||
50,
|
||||
75,
|
||||
100,
|
||||
125,
|
||||
150,
|
||||
200,
|
||||
]),
|
||||
default: 'auto',
|
||||
renderTrigger: true,
|
||||
description: t(
|
||||
'Left margin, in pixels, allowing for more room for axis labels',
|
||||
),
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'bottom_margin',
|
||||
config: {
|
||||
type: 'SelectControl',
|
||||
clearable: false,
|
||||
freeForm: true,
|
||||
label: t('Bottom Margin'),
|
||||
choices: formatSelectOptions([
|
||||
'auto',
|
||||
50,
|
||||
75,
|
||||
100,
|
||||
125,
|
||||
150,
|
||||
200,
|
||||
]),
|
||||
default: 'auto',
|
||||
renderTrigger: true,
|
||||
description: t(
|
||||
'Bottom margin, in pixels, allowing for more room for axis labels',
|
||||
),
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'y_axis_bounds',
|
||||
config: {
|
||||
type: 'BoundsControl',
|
||||
label: t('Value bounds'),
|
||||
renderTrigger: true,
|
||||
default: [null, null],
|
||||
description: t(
|
||||
'Hard value bounds applied for color coding. Is only relevant ' +
|
||||
'and applied when the normalization is applied against the whole heatmap.',
|
||||
),
|
||||
},
|
||||
},
|
||||
],
|
||||
['y_axis_format'],
|
||||
[
|
||||
{
|
||||
name: 'sort_x_axis',
|
||||
config: {
|
||||
type: 'SelectControl',
|
||||
label: t('Sort X Axis'),
|
||||
choices: sortAxisChoices,
|
||||
clearable: false,
|
||||
default: 'alpha_asc',
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'sort_y_axis',
|
||||
config: {
|
||||
type: 'SelectControl',
|
||||
label: t('Sort Y Axis'),
|
||||
choices: sortAxisChoices,
|
||||
clearable: false,
|
||||
default: 'alpha_asc',
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'show_legend',
|
||||
config: {
|
||||
type: 'CheckboxControl',
|
||||
label: t('Legend'),
|
||||
renderTrigger: true,
|
||||
default: false,
|
||||
description: t('Whether to display the legend (toggles)'),
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'show_perc',
|
||||
config: {
|
||||
type: 'CheckboxControl',
|
||||
label: t('Show percentage'),
|
||||
renderTrigger: true,
|
||||
description: t(
|
||||
'Whether to include the percentage in the tooltip',
|
||||
),
|
||||
default: true,
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'show_values',
|
||||
config: {
|
||||
type: 'CheckboxControl',
|
||||
label: t('Show Values'),
|
||||
renderTrigger: true,
|
||||
default: false,
|
||||
description: t(
|
||||
'Whether to display the numerical values within the cells',
|
||||
),
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'normalized',
|
||||
config: {
|
||||
type: 'CheckboxControl',
|
||||
label: t('Normalized'),
|
||||
renderTrigger: true,
|
||||
description: t(
|
||||
'Whether to apply a normal distribution based on rank on the color scale',
|
||||
),
|
||||
default: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
],
|
||||
controlOverrides: {
|
||||
y_axis_format: {
|
||||
label: t('Value Format'),
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export default config;
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 108 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 46 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 36 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 124 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 38 KiB |
@@ -0,0 +1,60 @@
|
||||
/**
|
||||
* 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, ChartMetadata, ChartPlugin } from '@superset-ui/core';
|
||||
import transformProps from './transformProps';
|
||||
import transportation from './images/transportation.jpg';
|
||||
import channels from './images/channels.jpg';
|
||||
import employment from './images/employment.jpg';
|
||||
import thumbnail from './images/thumbnail.png';
|
||||
import controlPanel from './controlPanel';
|
||||
|
||||
const metadata = new ChartMetadata({
|
||||
category: t('Correlation'),
|
||||
credits: ['http://bl.ocks.org/mbostock/3074470'],
|
||||
description: t(
|
||||
'Visualize a related metric across pairs of groups. Heatmaps excel at showcasing the correlation or strength between two groups. Color is used to emphasize the strength of the link between each pair of groups.',
|
||||
),
|
||||
exampleGallery: [
|
||||
{ url: transportation, caption: t('Sizes of vehicles') },
|
||||
{ url: channels, caption: t('Relationships between community channels') },
|
||||
{ url: employment, caption: t('Employment and education') },
|
||||
],
|
||||
name: t('Heatmap'),
|
||||
tags: [
|
||||
t('Business'),
|
||||
t('Intensity'),
|
||||
t('Legacy'),
|
||||
t('Density'),
|
||||
t('Predictive'),
|
||||
t('Single Metric'),
|
||||
],
|
||||
thumbnail,
|
||||
useLegacyApi: true,
|
||||
});
|
||||
|
||||
export default class HeatmapChartPlugin extends ChartPlugin {
|
||||
constructor() {
|
||||
super({
|
||||
loadChart: () => import('./ReactHeatmap'),
|
||||
metadata,
|
||||
transformProps,
|
||||
controlPanel,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
export default function transformProps(chartProps) {
|
||||
const { width, height, formData, queriesData } = chartProps;
|
||||
const {
|
||||
bottomMargin,
|
||||
canvasImageRendering,
|
||||
allColumnsX,
|
||||
allColumnsY,
|
||||
linearColorScheme,
|
||||
leftMargin,
|
||||
metric,
|
||||
normalized,
|
||||
showLegend,
|
||||
showPerc,
|
||||
showValues,
|
||||
sortXAxis,
|
||||
sortYAxis,
|
||||
xscaleInterval,
|
||||
yscaleInterval,
|
||||
yAxisBounds,
|
||||
yAxisFormat,
|
||||
} = formData;
|
||||
|
||||
return {
|
||||
width,
|
||||
height,
|
||||
data: queriesData[0].data,
|
||||
bottomMargin,
|
||||
canvasImageRendering,
|
||||
colorScheme: linearColorScheme,
|
||||
columnX: allColumnsX,
|
||||
columnY: allColumnsY,
|
||||
leftMargin,
|
||||
metric,
|
||||
normalized,
|
||||
numberFormat: yAxisFormat,
|
||||
showLegend,
|
||||
showPercentage: showPerc,
|
||||
showValues,
|
||||
sortXAxis,
|
||||
sortYAxis,
|
||||
xScaleInterval: parseInt(xscaleInterval, 10),
|
||||
yScaleInterval: parseInt(yscaleInterval, 10),
|
||||
yAxisBounds,
|
||||
};
|
||||
}
|
||||
74
superset-frontend/plugins/legacy-plugin-chart-heatmap/src/vendor/d3tip.css
vendored
Normal file
74
superset-frontend/plugins/legacy-plugin-chart-heatmap/src/vendor/d3tip.css
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
/* from d3-tip */
|
||||
.d3-tip {
|
||||
line-height: 1;
|
||||
padding: 12px;
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
color: #fff;
|
||||
border-radius: 2px;
|
||||
pointer-events: none;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
/* Creates a small triangle extender for the tooltip */
|
||||
.d3-tip:after {
|
||||
box-sizing: border-box;
|
||||
display: inline;
|
||||
font-size: 10px;
|
||||
width: 100%;
|
||||
line-height: 1;
|
||||
color: rgba(0, 0, 0, 0.8);
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Northward tooltips */
|
||||
.d3-tip.n:after {
|
||||
content: '\25BC';
|
||||
margin: -1px 0 0 0;
|
||||
top: 100%;
|
||||
left: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Eastward tooltips */
|
||||
.d3-tip.e:after {
|
||||
content: '\25C0';
|
||||
margin: -4px 0 0 0;
|
||||
top: 50%;
|
||||
left: -8px;
|
||||
}
|
||||
|
||||
/* Southward tooltips */
|
||||
.d3-tip.s:after {
|
||||
content: '\25B2';
|
||||
margin: 0 0 1px 0;
|
||||
top: -8px;
|
||||
left: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Westward tooltips */
|
||||
.d3-tip.w:after {
|
||||
content: '\25B6';
|
||||
margin: -4px 0 0 -1px;
|
||||
top: 50%;
|
||||
left: 100%;
|
||||
}
|
||||
Reference in New Issue
Block a user