mirror of
https://github.com/apache/superset.git
synced 2024-09-12 00:29:39 -04:00
feat: Adds the ECharts Sunburst chart (#22833)
This commit is contained in:
parent
cd6fc35f60
commit
30abefb519
@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* 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 React from 'react';
|
||||||
|
import { SuperChart, getChartTransformPropsRegistry } from '@superset-ui/core';
|
||||||
|
import { boolean, withKnobs } from '@storybook/addon-knobs';
|
||||||
|
import {
|
||||||
|
EchartsSunburstChartPlugin,
|
||||||
|
SunburstTransformProps,
|
||||||
|
} from '@superset-ui/plugin-chart-echarts';
|
||||||
|
import { withResizableChartDemo } from '../../../../shared/components/ResizableChartDemo';
|
||||||
|
import data from './data';
|
||||||
|
|
||||||
|
new EchartsSunburstChartPlugin()
|
||||||
|
.configure({ key: 'echarts-sunburst' })
|
||||||
|
.register();
|
||||||
|
|
||||||
|
getChartTransformPropsRegistry().registerValue(
|
||||||
|
'echarts-sunburst',
|
||||||
|
SunburstTransformProps,
|
||||||
|
);
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'Chart Plugins/plugin-chart-echarts/Sunburst',
|
||||||
|
decorators: [withKnobs, withResizableChartDemo],
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Sunburst = ({ width, height }) => (
|
||||||
|
<SuperChart
|
||||||
|
chartType="echarts-sunburst"
|
||||||
|
width={width}
|
||||||
|
height={height}
|
||||||
|
queriesData={[{ data }]}
|
||||||
|
formData={{
|
||||||
|
columns: ['genre', 'platform'],
|
||||||
|
metric: 'count',
|
||||||
|
showLabels: boolean('Show labels', true),
|
||||||
|
showTotal: boolean('Show total', true),
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
@ -0,0 +1,32 @@
|
|||||||
|
/*
|
||||||
|
* 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 [
|
||||||
|
{ genre: 'Adventure', platform: 'Wii', count: 84 },
|
||||||
|
{ genre: 'Adventure', platform: 'N64', count: 14 },
|
||||||
|
{ genre: 'Adventure', platform: 'XOne', count: 12 },
|
||||||
|
{ genre: 'Adventure', platform: 'PS4', count: 19 },
|
||||||
|
{ genre: 'Strategy', platform: 'Wii', count: 25 },
|
||||||
|
{ genre: 'Strategy', platform: 'PS4', count: 15 },
|
||||||
|
{ genre: 'Strategy', platform: 'N64', count: 29 },
|
||||||
|
{ genre: 'Strategy', platform: 'XOne', count: 23 },
|
||||||
|
{ genre: 'Simulation', platform: 'PS4', count: 15 },
|
||||||
|
{ genre: 'Simulation', platform: 'XOne', count: 36 },
|
||||||
|
{ genre: 'Simulation', platform: 'N64', count: 20 },
|
||||||
|
{ genre: 'Simulation', platform: 'Wii', count: 50 },
|
||||||
|
];
|
@ -94,22 +94,22 @@ export default function EchartsSunburst(props: SunburstTransformedProps) {
|
|||||||
contextmenu: eventParams => {
|
contextmenu: eventParams => {
|
||||||
if (onContextMenu) {
|
if (onContextMenu) {
|
||||||
eventParams.event.stop();
|
eventParams.event.stop();
|
||||||
|
const { data } = eventParams;
|
||||||
|
const { records } = data;
|
||||||
const treePath = extractTreePathInfo(eventParams.treePathInfo);
|
const treePath = extractTreePathInfo(eventParams.treePathInfo);
|
||||||
if (treePath.length > 0) {
|
const pointerEvent = eventParams.event.event;
|
||||||
const pointerEvent = eventParams.event.event;
|
const filters: BinaryQueryObjectFilterClause[] = [];
|
||||||
const filters: BinaryQueryObjectFilterClause[] = [];
|
if (columns?.length) {
|
||||||
if (columns) {
|
treePath.forEach((path, i) =>
|
||||||
treePath.forEach((path, i) =>
|
filters.push({
|
||||||
filters.push({
|
col: columns[i],
|
||||||
col: columns[i],
|
op: '==',
|
||||||
op: '==',
|
val: records[i],
|
||||||
val: path,
|
formattedVal: path,
|
||||||
formattedVal: path,
|
}),
|
||||||
}),
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
onContextMenu(pointerEvent.clientX, pointerEvent.clientY, filters);
|
|
||||||
}
|
}
|
||||||
|
onContextMenu(pointerEvent.clientX, pointerEvent.clientY, filters);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -189,9 +189,10 @@ const config: ControlPanelConfig = {
|
|||||||
controls?.secondary_metric?.value !== controls?.metric.value,
|
controls?.secondary_metric?.value !== controls?.metric.value,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
groupby: {
|
columns: {
|
||||||
label: t('Hierarchy'),
|
label: t('Hierarchy'),
|
||||||
description: t('This defines the level of the hierarchy'),
|
description: t(`Sets the hierarchy levels of the chart. Each level is
|
||||||
|
represented by one ring with the innermost circle as the top of the hierarchy.`),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
formDataOverrides: formData => ({
|
formDataOverrides: formData => ({
|
||||||
|
Binary file not shown.
After Width: | Height: | Size: 127 KiB |
Binary file not shown.
After Width: | Height: | Size: 123 KiB |
Binary file not shown.
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 127 KiB |
@ -21,6 +21,8 @@ import transformProps from './transformProps';
|
|||||||
import thumbnail from './images/thumbnail.png';
|
import thumbnail from './images/thumbnail.png';
|
||||||
import controlPanel from './controlPanel';
|
import controlPanel from './controlPanel';
|
||||||
import buildQuery from './buildQuery';
|
import buildQuery from './buildQuery';
|
||||||
|
import example1 from './images/Sunburst1.png';
|
||||||
|
import example2 from './images/Sunburst2.png';
|
||||||
|
|
||||||
export default class EchartsSunburstChartPlugin extends ChartPlugin {
|
export default class EchartsSunburstChartPlugin extends ChartPlugin {
|
||||||
constructor() {
|
constructor() {
|
||||||
@ -29,13 +31,13 @@ export default class EchartsSunburstChartPlugin extends ChartPlugin {
|
|||||||
controlPanel,
|
controlPanel,
|
||||||
loadChart: () => import('./EchartsSunburst'),
|
loadChart: () => import('./EchartsSunburst'),
|
||||||
metadata: new ChartMetadata({
|
metadata: new ChartMetadata({
|
||||||
behaviors: [Behavior.INTERACTIVE_CHART],
|
behaviors: [Behavior.INTERACTIVE_CHART, Behavior.DRILL_TO_DETAIL],
|
||||||
category: t('Part of a Whole'),
|
category: t('Part of a Whole'),
|
||||||
credits: ['https://echarts.apache.org'],
|
credits: ['https://echarts.apache.org'],
|
||||||
description: t(
|
description: t(
|
||||||
'Uses circles to visualize the flow of data through different stages of a system. Hover over individual paths in the visualization to understand the stages a value took. Useful for multi-stage, multi-group visualizing funnels and pipelines.',
|
'Uses circles to visualize the flow of data through different stages of a system. Hover over individual paths in the visualization to understand the stages a value took. Useful for multi-stage, multi-group visualizing funnels and pipelines.',
|
||||||
),
|
),
|
||||||
exampleGallery: [],
|
exampleGallery: [{ url: example1 }, { url: example2 }],
|
||||||
name: t('Sunburst Chart v2'),
|
name: t('Sunburst Chart v2'),
|
||||||
tags: [
|
tags: [
|
||||||
t('ECharts'),
|
t('ECharts'),
|
||||||
|
@ -16,9 +16,9 @@
|
|||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {
|
import {
|
||||||
CategoricalColorNamespace,
|
CategoricalColorNamespace,
|
||||||
|
DataRecordValue,
|
||||||
getColumnLabel,
|
getColumnLabel,
|
||||||
getMetricLabel,
|
getMetricLabel,
|
||||||
getNumberFormatter,
|
getNumberFormatter,
|
||||||
@ -26,21 +26,23 @@ import {
|
|||||||
getTimeFormatter,
|
getTimeFormatter,
|
||||||
NumberFormats,
|
NumberFormats,
|
||||||
NumberFormatter,
|
NumberFormatter,
|
||||||
|
SupersetTheme,
|
||||||
t,
|
t,
|
||||||
} from '@superset-ui/core';
|
} from '@superset-ui/core';
|
||||||
import { EChartsCoreOption } from 'echarts';
|
import { EChartsCoreOption } from 'echarts';
|
||||||
import { SunburstSeriesNodeItemOption } from 'echarts/types/src/chart/sunburst/SunburstSeries';
|
|
||||||
import { CallbackDataParams } from 'echarts/types/src/util/types';
|
import { CallbackDataParams } from 'echarts/types/src/util/types';
|
||||||
import { OpacityEnum } from '../constants';
|
import { OpacityEnum } from '../constants';
|
||||||
import { defaultGrid, defaultTooltip } from '../defaults';
|
import { defaultGrid } from '../defaults';
|
||||||
import { Refs } from '../types';
|
import { Refs } from '../types';
|
||||||
import { formatSeriesName, getColtypesMapping } from '../utils/series';
|
import { formatSeriesName, getColtypesMapping } from '../utils/series';
|
||||||
import { treeBuilder, TreeNode } from '../utils/treeBuilder';
|
import { treeBuilder, TreeNode } from '../utils/treeBuilder';
|
||||||
import {
|
import {
|
||||||
EchartsSunburstChartProps,
|
EchartsSunburstChartProps,
|
||||||
EchartsSunburstLabelType,
|
EchartsSunburstLabelType,
|
||||||
|
NodeItemOption,
|
||||||
SunburstTransformedProps,
|
SunburstTransformedProps,
|
||||||
} from './types';
|
} from './types';
|
||||||
|
import { getDefaultTooltip } from '../utils/tooltip';
|
||||||
|
|
||||||
export function getLinearDomain(
|
export function getLinearDomain(
|
||||||
treeData: TreeNode[],
|
treeData: TreeNode[],
|
||||||
@ -96,6 +98,7 @@ export function formatTooltip({
|
|||||||
totalValue,
|
totalValue,
|
||||||
metricLabel,
|
metricLabel,
|
||||||
secondaryMetricLabel,
|
secondaryMetricLabel,
|
||||||
|
theme,
|
||||||
}: {
|
}: {
|
||||||
params: CallbackDataParams & {
|
params: CallbackDataParams & {
|
||||||
treePathInfo: {
|
treePathInfo: {
|
||||||
@ -109,9 +112,9 @@ export function formatTooltip({
|
|||||||
totalValue: number;
|
totalValue: number;
|
||||||
metricLabel: string;
|
metricLabel: string;
|
||||||
secondaryMetricLabel?: string;
|
secondaryMetricLabel?: string;
|
||||||
|
theme: SupersetTheme;
|
||||||
}): string {
|
}): string {
|
||||||
const { data, treePathInfo = [] } = params;
|
const { data, treePathInfo = [] } = params;
|
||||||
treePathInfo.shift();
|
|
||||||
const node = data as TreeNode;
|
const node = data as TreeNode;
|
||||||
const formattedValue = numberFormatter(node.value);
|
const formattedValue = numberFormatter(node.value);
|
||||||
const formattedSecondaryValue = numberFormatter(node.secondaryValue);
|
const formattedSecondaryValue = numberFormatter(node.secondaryValue);
|
||||||
@ -121,33 +124,43 @@ export function formatTooltip({
|
|||||||
node.secondaryValue / node.value,
|
node.secondaryValue / node.value,
|
||||||
);
|
);
|
||||||
const absolutePercentage = percentFormatter(node.value / totalValue);
|
const absolutePercentage = percentFormatter(node.value / totalValue);
|
||||||
const parentNode = treePathInfo[treePathInfo.length - 1];
|
const parentNode =
|
||||||
|
treePathInfo.length > 2 ? treePathInfo[treePathInfo.length - 2] : undefined;
|
||||||
|
|
||||||
const result = [
|
const result = [
|
||||||
`<div style="font-size: 14px;font-weight: 600">${absolutePercentage} of total</div>`,
|
`<div style="
|
||||||
|
font-size: ${theme.typography.sizes.m}px;
|
||||||
|
color: ${theme.colors.grayscale.base}"
|
||||||
|
>`,
|
||||||
|
`<div style="font-weight: ${theme.typography.weights.bold}">
|
||||||
|
${node.name}
|
||||||
|
</div>`,
|
||||||
|
`<div">
|
||||||
|
${absolutePercentage} of total
|
||||||
|
</div>`,
|
||||||
];
|
];
|
||||||
if (parentNode) {
|
if (parentNode) {
|
||||||
const conditionalPercentage = percentFormatter(
|
const conditionalPercentage = percentFormatter(
|
||||||
node.value / parentNode.value,
|
node.value / parentNode.value,
|
||||||
);
|
);
|
||||||
result.push(`
|
result.push(`
|
||||||
<div style="font-size: 12px;">
|
<div>
|
||||||
${conditionalPercentage} of parent
|
${conditionalPercentage} of ${parentNode.name}
|
||||||
</div>`);
|
</div>`);
|
||||||
}
|
}
|
||||||
result.push(
|
result.push(
|
||||||
`<div style="color: '#666666'">
|
`<div>
|
||||||
${metricLabel}: ${formattedValue}${
|
${metricLabel}: ${formattedValue}${
|
||||||
colorByCategory
|
colorByCategory
|
||||||
? ''
|
? ''
|
||||||
: `, ${secondaryMetricLabel}: ${formattedSecondaryValue}`
|
: `, ${secondaryMetricLabel}: ${formattedSecondaryValue}`
|
||||||
}
|
}
|
||||||
</div>`,
|
</div>`,
|
||||||
colorByCategory
|
colorByCategory
|
||||||
? ''
|
? ''
|
||||||
: `<div style="color: '#666666'">
|
: `<div>${metricLabel}/${secondaryMetricLabel}: ${compareValuePercentage}</div>`,
|
||||||
${metricLabel}/${secondaryMetricLabel}: ${compareValuePercentage}
|
|
||||||
</div>`,
|
|
||||||
);
|
);
|
||||||
|
result.push('</div>');
|
||||||
return result.join('\n');
|
return result.join('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -247,9 +260,14 @@ export default function transformProps(
|
|||||||
linearColorScale(totalSecondaryValue / totalValue);
|
linearColorScale(totalSecondaryValue / totalValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
const traverse = (treeNodes: TreeNode[], path: string[]) =>
|
const traverse = (
|
||||||
|
treeNodes: TreeNode[],
|
||||||
|
path: string[],
|
||||||
|
pathRecords?: DataRecordValue[],
|
||||||
|
) =>
|
||||||
treeNodes.map(treeNode => {
|
treeNodes.map(treeNode => {
|
||||||
const { name: nodeName, value, secondaryValue, groupBy } = treeNode;
|
const { name: nodeName, value, secondaryValue, groupBy } = treeNode;
|
||||||
|
const records = [...(pathRecords || []), nodeName];
|
||||||
let name = formatSeriesName(nodeName, {
|
let name = formatSeriesName(nodeName, {
|
||||||
numberFormatter,
|
numberFormatter,
|
||||||
timeFormatter: getTimeFormatter(dateFormat),
|
timeFormatter: getTimeFormatter(dateFormat),
|
||||||
@ -258,10 +276,10 @@ export default function transformProps(
|
|||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
const newPath = path.concat(name);
|
const newPath = path.concat(name);
|
||||||
let item: SunburstSeriesNodeItemOption = {
|
let item: NodeItemOption = {
|
||||||
|
records,
|
||||||
name,
|
name,
|
||||||
value,
|
value,
|
||||||
// @ts-ignore
|
|
||||||
secondaryValue,
|
secondaryValue,
|
||||||
itemStyle: {
|
itemStyle: {
|
||||||
color: colorByCategory
|
color: colorByCategory
|
||||||
@ -270,7 +288,7 @@ export default function transformProps(
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
if (treeNode.children?.length) {
|
if (treeNode.children?.length) {
|
||||||
item.children = traverse(treeNode.children, newPath);
|
item.children = traverse(treeNode.children, newPath, records);
|
||||||
} else {
|
} else {
|
||||||
name = newPath.join(',');
|
name = newPath.join(',');
|
||||||
}
|
}
|
||||||
@ -295,7 +313,7 @@ export default function transformProps(
|
|||||||
...defaultGrid,
|
...defaultGrid,
|
||||||
},
|
},
|
||||||
tooltip: {
|
tooltip: {
|
||||||
...defaultTooltip,
|
...getDefaultTooltip(refs),
|
||||||
show: !inContextMenu,
|
show: !inContextMenu,
|
||||||
trigger: 'item',
|
trigger: 'item',
|
||||||
formatter: (params: any) =>
|
formatter: (params: any) =>
|
||||||
@ -306,6 +324,7 @@ export default function transformProps(
|
|||||||
totalValue,
|
totalValue,
|
||||||
metricLabel,
|
metricLabel,
|
||||||
secondaryMetricLabel,
|
secondaryMetricLabel,
|
||||||
|
theme,
|
||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
series: [
|
series: [
|
||||||
|
@ -20,10 +20,12 @@
|
|||||||
import {
|
import {
|
||||||
ChartDataResponseResult,
|
ChartDataResponseResult,
|
||||||
ChartProps,
|
ChartProps,
|
||||||
|
DataRecordValue,
|
||||||
QueryFormColumn,
|
QueryFormColumn,
|
||||||
QueryFormData,
|
QueryFormData,
|
||||||
QueryFormMetric,
|
QueryFormMetric,
|
||||||
} from '@superset-ui/core';
|
} from '@superset-ui/core';
|
||||||
|
import { SunburstSeriesNodeItemOption } from 'echarts/types/src/chart/sunburst/SunburstSeries';
|
||||||
import {
|
import {
|
||||||
BaseTransformedProps,
|
BaseTransformedProps,
|
||||||
ContextMenuTransformedProps,
|
ContextMenuTransformedProps,
|
||||||
@ -62,3 +64,8 @@ export type SunburstTransformedProps =
|
|||||||
BaseTransformedProps<EchartsSunburstFormData> &
|
BaseTransformedProps<EchartsSunburstFormData> &
|
||||||
ContextMenuTransformedProps &
|
ContextMenuTransformedProps &
|
||||||
CrossFilterTransformedProps;
|
CrossFilterTransformedProps;
|
||||||
|
|
||||||
|
export type NodeItemOption = SunburstSeriesNodeItemOption & {
|
||||||
|
records: DataRecordValue[];
|
||||||
|
secondaryValue: number;
|
||||||
|
};
|
||||||
|
@ -1,7 +1,3 @@
|
|||||||
import { CallbackDataParams } from 'echarts/types/src/util/types';
|
|
||||||
import { LegendOrientation } from './types';
|
|
||||||
import { TOOLTIP_POINTER_MARGIN, TOOLTIP_OVERFLOW_MARGIN } from './constants';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Licensed to the Apache Software Foundation (ASF) under one
|
* Licensed to the Apache Software Foundation (ASF) under one
|
||||||
* or more contributor license agreements. See the NOTICE file
|
* or more contributor license agreements. See the NOTICE file
|
||||||
@ -20,66 +16,12 @@ import { TOOLTIP_POINTER_MARGIN, TOOLTIP_OVERFLOW_MARGIN } from './constants';
|
|||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
|
import { LegendOrientation } from './types';
|
||||||
|
|
||||||
export const defaultGrid = {
|
export const defaultGrid = {
|
||||||
containLabel: true,
|
containLabel: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const defaultTooltip = {
|
|
||||||
position: (
|
|
||||||
canvasMousePos: [number, number],
|
|
||||||
params: CallbackDataParams,
|
|
||||||
tooltipDom: HTMLElement,
|
|
||||||
rect: any,
|
|
||||||
sizes: { contentSize: [number, number]; viewSize: [number, number] },
|
|
||||||
) => {
|
|
||||||
// algorithm copy-pasted from here:
|
|
||||||
// https://github.com/apache/echarts/issues/5004#issuecomment-559668309
|
|
||||||
|
|
||||||
// The chart canvas position
|
|
||||||
const canvasRect = tooltipDom.parentElement
|
|
||||||
?.getElementsByTagName('canvas')[0]
|
|
||||||
.getBoundingClientRect();
|
|
||||||
|
|
||||||
// The mouse coordinates relative to the whole window
|
|
||||||
// The first parameter to the position function is the mouse position relative to the canvas
|
|
||||||
const mouseX = canvasMousePos[0] + (canvasRect?.x || 0);
|
|
||||||
const mouseY = canvasMousePos[1] + (canvasRect?.y || 0);
|
|
||||||
|
|
||||||
// The width and height of the tooltip dom element
|
|
||||||
const tooltipWidth = sizes.contentSize[0];
|
|
||||||
const tooltipHeight = sizes.contentSize[1];
|
|
||||||
|
|
||||||
// Start by placing the tooltip top and right relative to the mouse position
|
|
||||||
let xPos = mouseX + TOOLTIP_POINTER_MARGIN;
|
|
||||||
let yPos = mouseY - TOOLTIP_POINTER_MARGIN - tooltipHeight;
|
|
||||||
|
|
||||||
// The tooltip is overflowing past the right edge of the window
|
|
||||||
if (xPos + tooltipWidth >= document.documentElement.clientWidth) {
|
|
||||||
// Attempt to place the tooltip to the left of the mouse position
|
|
||||||
xPos = mouseX - TOOLTIP_POINTER_MARGIN - tooltipWidth;
|
|
||||||
|
|
||||||
// The tooltip is overflowing past the left edge of the window
|
|
||||||
if (xPos <= 0)
|
|
||||||
// Place the tooltip a fixed distance from the left edge of the window
|
|
||||||
xPos = TOOLTIP_OVERFLOW_MARGIN;
|
|
||||||
}
|
|
||||||
|
|
||||||
// The tooltip is overflowing past the top edge of the window
|
|
||||||
if (yPos <= 0) {
|
|
||||||
// Attempt to place the tooltip to the bottom of the mouse position
|
|
||||||
yPos = mouseY + TOOLTIP_POINTER_MARGIN;
|
|
||||||
|
|
||||||
// The tooltip is overflowing past the bottom edge of the window
|
|
||||||
if (yPos + tooltipHeight >= document.documentElement.clientHeight)
|
|
||||||
// Place the tooltip a fixed distance from the top edge of the window
|
|
||||||
yPos = TOOLTIP_OVERFLOW_MARGIN;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the position (converted back to a relative position on the canvas)
|
|
||||||
return [xPos - (canvasRect?.x || 0), yPos - (canvasRect?.y || 0)];
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export const defaultYAxis = {
|
export const defaultYAxis = {
|
||||||
scale: true,
|
scale: true,
|
||||||
};
|
};
|
||||||
|
@ -16,17 +16,21 @@
|
|||||||
* specific language governing permissions and limitations
|
* specific language governing permissions and limitations
|
||||||
* under the License.
|
* under the License.
|
||||||
*/
|
*/
|
||||||
import { DataRecord } from '@superset-ui/core';
|
import { DataRecord, DataRecordValue } from '@superset-ui/core';
|
||||||
import _ from 'lodash';
|
import _ from 'lodash';
|
||||||
|
|
||||||
export type TreeNode = {
|
export type TreeNode = {
|
||||||
name: string;
|
name: DataRecordValue;
|
||||||
value: number;
|
value: number;
|
||||||
secondaryValue: number;
|
secondaryValue: number;
|
||||||
groupBy: string;
|
groupBy: string;
|
||||||
children?: TreeNode[];
|
children?: TreeNode[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function getMetricValue(datum: DataRecord, metric: string) {
|
||||||
|
return _.isNumber(datum[metric]) ? (datum[metric] as number) : 0;
|
||||||
|
}
|
||||||
|
|
||||||
export function treeBuilder(
|
export function treeBuilder(
|
||||||
data: DataRecord[],
|
data: DataRecord[],
|
||||||
groupBy: string[],
|
groupBy: string[],
|
||||||
@ -37,7 +41,8 @@ export function treeBuilder(
|
|||||||
const curData = _.groupBy(data, curGroupBy);
|
const curData = _.groupBy(data, curGroupBy);
|
||||||
return _.transform(
|
return _.transform(
|
||||||
curData,
|
curData,
|
||||||
(result, value, name) => {
|
(result, value, key) => {
|
||||||
|
const name = curData[key][0][curGroupBy]!;
|
||||||
if (!restGroupby.length) {
|
if (!restGroupby.length) {
|
||||||
(value ?? []).forEach(datum => {
|
(value ?? []).forEach(datum => {
|
||||||
const metricValue = getMetricValue(datum, metric);
|
const metricValue = getMetricValue(datum, metric);
|
||||||
@ -81,7 +86,3 @@ export function treeBuilder(
|
|||||||
[] as TreeNode[],
|
[] as TreeNode[],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function getMetricValue(datum: DataRecord, metric: string) {
|
|
||||||
return _.isNumber(datum[metric]) ? (datum[metric] as number) : 0;
|
|
||||||
}
|
|
||||||
|
@ -68,6 +68,7 @@ import {
|
|||||||
EchartsTreemapChartPlugin,
|
EchartsTreemapChartPlugin,
|
||||||
EchartsMixedTimeseriesChartPlugin,
|
EchartsMixedTimeseriesChartPlugin,
|
||||||
EchartsTreeChartPlugin,
|
EchartsTreeChartPlugin,
|
||||||
|
EchartsSunburstChartPlugin,
|
||||||
} from '@superset-ui/plugin-chart-echarts';
|
} from '@superset-ui/plugin-chart-echarts';
|
||||||
import {
|
import {
|
||||||
SelectFilterPlugin,
|
SelectFilterPlugin,
|
||||||
@ -165,6 +166,7 @@ export default class MainPreset extends Preset {
|
|||||||
new TimeColumnFilterPlugin().configure({ key: 'filter_timecolumn' }),
|
new TimeColumnFilterPlugin().configure({ key: 'filter_timecolumn' }),
|
||||||
new TimeGrainFilterPlugin().configure({ key: 'filter_timegrain' }),
|
new TimeGrainFilterPlugin().configure({ key: 'filter_timegrain' }),
|
||||||
new EchartsTreeChartPlugin().configure({ key: 'tree_chart' }),
|
new EchartsTreeChartPlugin().configure({ key: 'tree_chart' }),
|
||||||
|
new EchartsSunburstChartPlugin().configure({ key: 'sunburst_v2' }),
|
||||||
new HandlebarsChartPlugin().configure({ key: 'handlebars' }),
|
new HandlebarsChartPlugin().configure({ key: 'handlebars' }),
|
||||||
...experimentalplugins,
|
...experimentalplugins,
|
||||||
],
|
],
|
||||||
|
Loading…
Reference in New Issue
Block a user