mirror of https://github.com/apache/superset.git
feat(plugin-chart-echarts): support horizontal bar chart (#19918)
* feat(plugin-chart-echarts): support horizontal bar chart * Update superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Bar/controlPanel.tsx Co-authored-by: Evan Rusackas <evan@preset.io> * Update superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts Co-authored-by: Ville Brofeldt <33317356+villebro@users.noreply.github.com> * Update superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/transformProps.ts Co-authored-by: Ville Brofeldt <33317356+villebro@users.noreply.github.com> * Update superset-frontend/plugins/plugin-chart-echarts/src/Timeseries/Regular/Bar/controlPanel.tsx Co-authored-by: Ville Brofeldt <33317356+villebro@users.noreply.github.com> * improve controlpanel * default value * fix ut Co-authored-by: Evan Rusackas <evan@preset.io> Co-authored-by: Ville Brofeldt <33317356+villebro@users.noreply.github.com>
This commit is contained in:
parent
d5802f7896
commit
9854d2d0e8
|
@ -21,8 +21,10 @@ import { t } from '@superset-ui/core';
|
|||
import { ControlPanelSectionConfig } from '../types';
|
||||
import { formatSelectOptions } from '../utils';
|
||||
|
||||
const TITLE_MARGIN_OPTIONS: number[] = [15, 30, 50, 75, 100, 125, 150, 200];
|
||||
const TITLE_POSITION_OPTIONS: string[] = ['Left', 'Top'];
|
||||
export const TITLE_MARGIN_OPTIONS: number[] = [
|
||||
15, 30, 50, 75, 100, 125, 150, 200,
|
||||
];
|
||||
export const TITLE_POSITION_OPTIONS: string[] = ['Left', 'Top'];
|
||||
export const titleControls: ControlPanelSectionConfig = {
|
||||
label: t('Chart Title'),
|
||||
tabOverride: 'customize',
|
||||
|
|
|
@ -21,8 +21,11 @@ import { FeatureFlag, isFeatureEnabled, t } from '@superset-ui/core';
|
|||
import {
|
||||
ControlPanelConfig,
|
||||
ControlPanelsContainerProps,
|
||||
ControlSetRow,
|
||||
ControlStateMapping,
|
||||
D3_TIME_FORMAT_DOCS,
|
||||
emitFilterControl,
|
||||
formatSelectOptions,
|
||||
sections,
|
||||
sharedControls,
|
||||
} from '@superset-ui/chart-controls';
|
||||
|
@ -30,6 +33,7 @@ import {
|
|||
import {
|
||||
DEFAULT_FORM_DATA,
|
||||
EchartsTimeseriesContributionType,
|
||||
OrientationType,
|
||||
} from '../../types';
|
||||
import {
|
||||
legendSection,
|
||||
|
@ -49,7 +53,217 @@ const {
|
|||
yAxisBounds,
|
||||
zoomable,
|
||||
xAxisLabelRotation,
|
||||
orientation,
|
||||
} = DEFAULT_FORM_DATA;
|
||||
|
||||
function createAxisTitleControl(axis: 'x' | 'y'): ControlSetRow[] {
|
||||
const isXAxis = axis === 'x';
|
||||
const isVertical = (controls: ControlStateMapping) =>
|
||||
Boolean(controls?.orientation.value === OrientationType.vertical);
|
||||
const isHorizental = (controls: ControlStateMapping) =>
|
||||
Boolean(controls?.orientation.value === OrientationType.horizontal);
|
||||
return [
|
||||
[
|
||||
{
|
||||
name: 'x_axis_title',
|
||||
config: {
|
||||
type: 'TextControl',
|
||||
label: t('Axis Title'),
|
||||
renderTrigger: true,
|
||||
default: '',
|
||||
description: t('Changing this control takes effect instantly'),
|
||||
visibility: ({ controls }: ControlPanelsContainerProps) =>
|
||||
isXAxis ? isVertical(controls) : isHorizental(controls),
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'x_axis_title_margin',
|
||||
config: {
|
||||
type: 'SelectControl',
|
||||
freeForm: true,
|
||||
clearable: true,
|
||||
label: t('AXIS TITLE MARGIN'),
|
||||
renderTrigger: true,
|
||||
default: sections.TITLE_MARGIN_OPTIONS[0],
|
||||
choices: formatSelectOptions(sections.TITLE_MARGIN_OPTIONS),
|
||||
description: t('Changing this control takes effect instantly'),
|
||||
visibility: ({ controls }: ControlPanelsContainerProps) =>
|
||||
isXAxis ? isVertical(controls) : isHorizental(controls),
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'y_axis_title',
|
||||
config: {
|
||||
type: 'TextControl',
|
||||
label: t('Axis Title'),
|
||||
renderTrigger: true,
|
||||
default: '',
|
||||
description: t('Changing this control takes effect instantly'),
|
||||
visibility: ({ controls }: ControlPanelsContainerProps) =>
|
||||
isXAxis ? isHorizental(controls) : isVertical(controls),
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'y_axis_title_margin',
|
||||
config: {
|
||||
type: 'SelectControl',
|
||||
freeForm: true,
|
||||
clearable: true,
|
||||
label: t('AXIS TITLE MARGIN'),
|
||||
renderTrigger: true,
|
||||
default: sections.TITLE_MARGIN_OPTIONS[0],
|
||||
choices: formatSelectOptions(sections.TITLE_MARGIN_OPTIONS),
|
||||
description: t('Changing this control takes effect instantly'),
|
||||
visibility: ({ controls }: ControlPanelsContainerProps) =>
|
||||
isXAxis ? isHorizental(controls) : isVertical(controls),
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'y_axis_title_position',
|
||||
config: {
|
||||
type: 'SelectControl',
|
||||
freeForm: true,
|
||||
clearable: false,
|
||||
label: t('AXIS TITLE POSITION'),
|
||||
renderTrigger: true,
|
||||
default: sections.TITLE_POSITION_OPTIONS[0],
|
||||
choices: formatSelectOptions(sections.TITLE_POSITION_OPTIONS),
|
||||
description: t('Changing this control takes effect instantly'),
|
||||
visibility: ({ controls }: ControlPanelsContainerProps) =>
|
||||
isXAxis ? isHorizental(controls) : isVertical(controls),
|
||||
},
|
||||
},
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
function createAxisControl(axis: 'x' | 'y'): ControlSetRow[] {
|
||||
const isXAxis = axis === 'x';
|
||||
const isVertical = (controls: ControlStateMapping) =>
|
||||
Boolean(controls?.orientation.value === OrientationType.vertical);
|
||||
const isHorizental = (controls: ControlStateMapping) =>
|
||||
Boolean(controls?.orientation.value === OrientationType.horizontal);
|
||||
return [
|
||||
[
|
||||
{
|
||||
name: 'x_axis_time_format',
|
||||
config: {
|
||||
...sharedControls.x_axis_time_format,
|
||||
default: 'smart_date',
|
||||
description: `${D3_TIME_FORMAT_DOCS}. ${t(
|
||||
'When using other than adaptive formatting, labels may overlap.',
|
||||
)}`,
|
||||
visibility: ({ controls }: ControlPanelsContainerProps) =>
|
||||
isXAxis ? isVertical(controls) : isHorizental(controls),
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'xAxisLabelRotation',
|
||||
config: {
|
||||
type: 'SelectControl',
|
||||
freeForm: true,
|
||||
clearable: false,
|
||||
label: t('Rotate axis label'),
|
||||
choices: [
|
||||
[0, '0°'],
|
||||
[45, '45°'],
|
||||
],
|
||||
default: xAxisLabelRotation,
|
||||
renderTrigger: true,
|
||||
description: t(
|
||||
'Input field supports custom rotation. e.g. 30 for 30°',
|
||||
),
|
||||
visibility: ({ controls }: ControlPanelsContainerProps) =>
|
||||
isXAxis ? isVertical(controls) : isHorizental(controls),
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'y_axis_format',
|
||||
config: {
|
||||
...sharedControls.y_axis_format,
|
||||
label: t('Axis Format'),
|
||||
visibility: ({ controls }: ControlPanelsContainerProps) =>
|
||||
isXAxis ? isHorizental(controls) : isVertical(controls),
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'logAxis',
|
||||
config: {
|
||||
type: 'CheckboxControl',
|
||||
label: t('Logarithmic axis'),
|
||||
renderTrigger: true,
|
||||
default: logAxis,
|
||||
description: t('Logarithmic axis'),
|
||||
visibility: ({ controls }: ControlPanelsContainerProps) =>
|
||||
isXAxis ? isHorizental(controls) : isVertical(controls),
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'minorSplitLine',
|
||||
config: {
|
||||
type: 'CheckboxControl',
|
||||
label: t('Minor Split Line'),
|
||||
renderTrigger: true,
|
||||
default: minorSplitLine,
|
||||
description: t('Draw split lines for minor axis ticks'),
|
||||
visibility: ({ controls }: ControlPanelsContainerProps) =>
|
||||
isXAxis ? isHorizental(controls) : isVertical(controls),
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'truncateYAxis',
|
||||
config: {
|
||||
type: 'CheckboxControl',
|
||||
label: t('Truncate Axis'),
|
||||
default: truncateYAxis,
|
||||
renderTrigger: true,
|
||||
description: t('It’s not recommended to truncate axis in Bar chart.'),
|
||||
visibility: ({ controls }: ControlPanelsContainerProps) =>
|
||||
isXAxis ? isHorizental(controls) : isVertical(controls),
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'y_axis_bounds',
|
||||
config: {
|
||||
type: 'BoundsControl',
|
||||
label: t('Axis Bounds'),
|
||||
renderTrigger: true,
|
||||
default: yAxisBounds,
|
||||
description: t(
|
||||
'Bounds for the axis. When left empty, the bounds are ' +
|
||||
'dynamically defined based on the min/max of the data. Note that ' +
|
||||
"this feature will only expand the axis range. It won't " +
|
||||
"narrow the data's extent.",
|
||||
),
|
||||
visibility: ({ controls }: ControlPanelsContainerProps) =>
|
||||
Boolean(controls?.truncateYAxis?.value) &&
|
||||
(isXAxis ? isHorizental(controls) : isVertical(controls)),
|
||||
},
|
||||
},
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
const config: ControlPanelConfig = {
|
||||
controlPanelSections: [
|
||||
sections.legacyTimeseriesTime,
|
||||
|
@ -87,7 +301,39 @@ const config: ControlPanelConfig = {
|
|||
sections.advancedAnalyticsControls,
|
||||
sections.annotationsAndLayersControls,
|
||||
sections.forecastIntervalControls,
|
||||
sections.titleControls,
|
||||
{
|
||||
label: t('Chart Orientation'),
|
||||
expanded: true,
|
||||
controlSetRows: [
|
||||
[
|
||||
{
|
||||
name: 'orientation',
|
||||
config: {
|
||||
type: 'RadioButtonControl',
|
||||
renderTrigger: true,
|
||||
label: t('Bar orientation'),
|
||||
default: orientation,
|
||||
options: [
|
||||
[OrientationType.vertical, t('Vertical')],
|
||||
[OrientationType.horizontal, t('Horizontal')],
|
||||
],
|
||||
description: t('Orientation of bar chart'),
|
||||
},
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
{
|
||||
label: t('Chart Title'),
|
||||
tabOverride: 'customize',
|
||||
expanded: true,
|
||||
controlSetRows: [
|
||||
[<div className="section-header">{t('X Axis')}</div>],
|
||||
...createAxisTitleControl('x'),
|
||||
[<div className="section-header">{t('Y Axis')}</div>],
|
||||
...createAxisTitleControl('y'),
|
||||
],
|
||||
},
|
||||
{
|
||||
label: t('Chart Options'),
|
||||
expanded: true,
|
||||
|
@ -140,101 +386,10 @@ const config: ControlPanelConfig = {
|
|||
],
|
||||
...legendSection,
|
||||
[<div className="section-header">{t('X Axis')}</div>],
|
||||
[
|
||||
{
|
||||
name: 'x_axis_time_format',
|
||||
config: {
|
||||
...sharedControls.x_axis_time_format,
|
||||
default: 'smart_date',
|
||||
description: `${D3_TIME_FORMAT_DOCS}. ${t(
|
||||
'When using other than adaptive formatting, labels may overlap.',
|
||||
)}`,
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'xAxisLabelRotation',
|
||||
config: {
|
||||
type: 'SelectControl',
|
||||
freeForm: true,
|
||||
clearable: false,
|
||||
label: t('Rotate x axis label'),
|
||||
choices: [
|
||||
[0, '0°'],
|
||||
[45, '45°'],
|
||||
],
|
||||
default: xAxisLabelRotation,
|
||||
renderTrigger: true,
|
||||
description: t(
|
||||
'Input field supports custom rotation. e.g. 30 for 30°',
|
||||
),
|
||||
},
|
||||
},
|
||||
],
|
||||
// eslint-disable-next-line react/jsx-key
|
||||
...createAxisControl('x'),
|
||||
...richTooltipSection,
|
||||
// eslint-disable-next-line react/jsx-key
|
||||
[<div className="section-header">{t('Y Axis')}</div>],
|
||||
|
||||
['y_axis_format'],
|
||||
[
|
||||
{
|
||||
name: 'logAxis',
|
||||
config: {
|
||||
type: 'CheckboxControl',
|
||||
label: t('Logarithmic y-axis'),
|
||||
renderTrigger: true,
|
||||
default: logAxis,
|
||||
description: t('Logarithmic y-axis'),
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'minorSplitLine',
|
||||
config: {
|
||||
type: 'CheckboxControl',
|
||||
label: t('Minor Split Line'),
|
||||
renderTrigger: true,
|
||||
default: minorSplitLine,
|
||||
description: t('Draw split lines for minor y-axis ticks'),
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'truncateYAxis',
|
||||
config: {
|
||||
type: 'CheckboxControl',
|
||||
label: t('Truncate Y Axis'),
|
||||
default: truncateYAxis,
|
||||
renderTrigger: true,
|
||||
description: t(
|
||||
'It’s not recommended to truncate y-axis in Bar chart.',
|
||||
),
|
||||
},
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'y_axis_bounds',
|
||||
config: {
|
||||
type: 'BoundsControl',
|
||||
label: t('Y Axis Bounds'),
|
||||
renderTrigger: true,
|
||||
default: yAxisBounds,
|
||||
description: t(
|
||||
'Bounds for the Y-axis. When left empty, the bounds are ' +
|
||||
'dynamically defined based on the min/max of the data. Note that ' +
|
||||
"this feature will only expand the axis range. It won't " +
|
||||
"narrow the data's extent.",
|
||||
),
|
||||
visibility: ({ controls }: ControlPanelsContainerProps) =>
|
||||
Boolean(controls?.truncateYAxis?.value),
|
||||
},
|
||||
},
|
||||
],
|
||||
...createAxisControl('y'),
|
||||
],
|
||||
},
|
||||
],
|
||||
|
|
|
@ -39,6 +39,7 @@ import {
|
|||
EchartsTimeseriesFormData,
|
||||
EchartsTimeseriesSeriesType,
|
||||
TimeseriesChartTransformedProps,
|
||||
OrientationType,
|
||||
} from './types';
|
||||
import { ForecastSeriesEnum, ForecastValue } from '../types';
|
||||
import { parseYAxisBound } from '../utils/controls';
|
||||
|
@ -138,16 +139,19 @@ export default function transformProps(
|
|||
yAxisTitlePosition,
|
||||
sliceId,
|
||||
timeGrainSqla,
|
||||
orientation,
|
||||
}: EchartsTimeseriesFormData = { ...DEFAULT_FORM_DATA, ...formData };
|
||||
|
||||
const colorScale = CategoricalColorNamespace.getScale(colorScheme as string);
|
||||
const rebasedData = rebaseForecastDatum(data, verboseMap);
|
||||
const xAxisCol =
|
||||
verboseMap[xAxisOrig] || getColumnLabel(xAxisOrig || DTTM_ALIAS);
|
||||
const isHorizontal = orientation === OrientationType.horizontal;
|
||||
const rawSeries = extractSeries(rebasedData, {
|
||||
fillNeighborValue: stack && !forecastEnabled ? 0 : undefined,
|
||||
xAxis: xAxisCol,
|
||||
removeNulls: seriesType === EchartsTimeseriesSeriesType.Scatter,
|
||||
isHorizontal,
|
||||
});
|
||||
const seriesContexts = extractForecastSeriesContexts(
|
||||
Object.values(rawSeries).map(series => series.name as string),
|
||||
|
@ -213,6 +217,7 @@ export default function transformProps(
|
|||
thresholdValues,
|
||||
richTooltip,
|
||||
sliceId,
|
||||
isHorizontal,
|
||||
});
|
||||
if (transformedSeries) series.push(transformedSeries);
|
||||
});
|
||||
|
@ -325,57 +330,66 @@ export default function transformProps(
|
|||
.map(entry => entry.name || '')
|
||||
.concat(extractAnnotationLabels(annotationLayers, annotationData));
|
||||
|
||||
let xAxis: any = {
|
||||
type: xAxisType,
|
||||
name: xAxisTitle,
|
||||
nameGap: convertInteger(xAxisTitleMargin),
|
||||
nameLocation: 'middle',
|
||||
axisLabel: {
|
||||
hideOverlap: true,
|
||||
formatter: xAxisFormatter,
|
||||
rotate: xAxisLabelRotation,
|
||||
},
|
||||
minInterval:
|
||||
xAxisType === 'time' && timeGrainSqla
|
||||
? TimeGrainToTimestamp[timeGrainSqla]
|
||||
: 0,
|
||||
};
|
||||
let yAxis: any = {
|
||||
...defaultYAxis,
|
||||
type: logAxis ? 'log' : 'value',
|
||||
min,
|
||||
max,
|
||||
minorTick: { show: true },
|
||||
minorSplitLine: { show: minorSplitLine },
|
||||
axisLabel: { formatter },
|
||||
scale: truncateYAxis,
|
||||
name: yAxisTitle,
|
||||
nameGap: convertInteger(yAxisTitleMargin),
|
||||
nameLocation: yAxisTitlePosition === 'Left' ? 'middle' : 'end',
|
||||
};
|
||||
|
||||
if (isHorizontal) {
|
||||
[xAxis, yAxis] = [yAxis, xAxis];
|
||||
[padding.bottom, padding.left] = [padding.left, padding.bottom];
|
||||
}
|
||||
|
||||
const echartOptions: EChartsCoreOption = {
|
||||
useUTC: true,
|
||||
grid: {
|
||||
...defaultGrid,
|
||||
...padding,
|
||||
},
|
||||
xAxis: {
|
||||
type: xAxisType,
|
||||
name: xAxisTitle,
|
||||
nameGap: convertInteger(xAxisTitleMargin),
|
||||
nameLocation: 'middle',
|
||||
axisLabel: {
|
||||
hideOverlap: true,
|
||||
formatter: xAxisFormatter,
|
||||
rotate: xAxisLabelRotation,
|
||||
},
|
||||
minInterval:
|
||||
xAxisType === 'time' && timeGrainSqla
|
||||
? TimeGrainToTimestamp[timeGrainSqla]
|
||||
: 0,
|
||||
},
|
||||
yAxis: {
|
||||
...defaultYAxis,
|
||||
type: logAxis ? 'log' : 'value',
|
||||
min,
|
||||
max,
|
||||
minorTick: { show: true },
|
||||
minorSplitLine: { show: minorSplitLine },
|
||||
axisLabel: { formatter },
|
||||
scale: truncateYAxis,
|
||||
name: yAxisTitle,
|
||||
nameGap: convertInteger(yAxisTitleMargin),
|
||||
nameLocation: yAxisTitlePosition === 'Left' ? 'middle' : 'end',
|
||||
},
|
||||
xAxis,
|
||||
yAxis,
|
||||
tooltip: {
|
||||
...defaultTooltip,
|
||||
appendToBody: true,
|
||||
trigger: richTooltip ? 'axis' : 'item',
|
||||
formatter: (params: any) => {
|
||||
const [xIndex, yIndex] = isHorizontal ? [1, 0] : [0, 1];
|
||||
const xValue: number = richTooltip
|
||||
? params[0].value[0]
|
||||
: params.value[0];
|
||||
? params[0].value[xIndex]
|
||||
: params.value[xIndex];
|
||||
const forecastValue: any[] = richTooltip ? params : [params];
|
||||
|
||||
if (richTooltip && tooltipSortByMetric) {
|
||||
forecastValue.sort((a, b) => b.data[1] - a.data[1]);
|
||||
forecastValue.sort((a, b) => b.data[yIndex] - a.data[yIndex]);
|
||||
}
|
||||
|
||||
const rows: Array<string> = [`${tooltipFormatter(xValue)}`];
|
||||
const forecastValues: Record<string, ForecastValue> =
|
||||
extractForecastValuesFromTooltipParams(forecastValue);
|
||||
extractForecastValuesFromTooltipParams(forecastValue, isHorizontal);
|
||||
|
||||
Object.keys(forecastValues).forEach(key => {
|
||||
const value = forecastValues[key];
|
||||
|
|
|
@ -86,6 +86,7 @@ export function transformSeries(
|
|||
richTooltip?: boolean;
|
||||
seriesKey?: OptionName;
|
||||
sliceId?: number;
|
||||
isHorizontal?: boolean;
|
||||
},
|
||||
): SeriesOption | undefined {
|
||||
const { name } = series;
|
||||
|
@ -108,6 +109,7 @@ export function transformSeries(
|
|||
richTooltip,
|
||||
seriesKey,
|
||||
sliceId,
|
||||
isHorizontal = false,
|
||||
} = opts;
|
||||
const contexts = seriesContexts[name || ''] || [];
|
||||
const hasForecast =
|
||||
|
@ -217,14 +219,10 @@ export function transformSeries(
|
|||
symbolSize: markerSize,
|
||||
label: {
|
||||
show: !!showValue,
|
||||
position: 'top',
|
||||
position: isHorizontal ? 'right' : 'top',
|
||||
formatter: (params: any) => {
|
||||
const {
|
||||
value: [, numericValue],
|
||||
dataIndex,
|
||||
seriesIndex,
|
||||
seriesName,
|
||||
} = params;
|
||||
const { value, dataIndex, seriesIndex, seriesName } = params;
|
||||
const numericValue = isHorizontal ? value[0] : value[1];
|
||||
const isSelectedLegend = currentSeries.legend === seriesName;
|
||||
if (!formatter) return numericValue;
|
||||
if (!stack || isSelectedLegend) return formatter(numericValue);
|
||||
|
|
|
@ -38,6 +38,11 @@ export enum EchartsTimeseriesContributionType {
|
|||
Column = 'column',
|
||||
}
|
||||
|
||||
export enum OrientationType {
|
||||
vertical = 'vertical',
|
||||
horizontal = 'horizontal',
|
||||
}
|
||||
|
||||
export enum EchartsTimeseriesSeriesType {
|
||||
Line = 'line',
|
||||
Scatter = 'scatter',
|
||||
|
@ -82,6 +87,7 @@ export type EchartsTimeseriesFormData = QueryFormData & {
|
|||
showValue: boolean;
|
||||
onlyTotal: boolean;
|
||||
percentageThreshold: number;
|
||||
orientation?: OrientationType;
|
||||
} & EchartsLegendFormData &
|
||||
EchartsTitleFormData;
|
||||
|
||||
|
@ -119,6 +125,7 @@ export const DEFAULT_FORM_DATA: EchartsTimeseriesFormData = {
|
|||
showValue: false,
|
||||
onlyTotal: false,
|
||||
percentageThreshold: 0,
|
||||
orientation: OrientationType.vertical,
|
||||
...DEFAULT_TITLE_FORM_DATA,
|
||||
};
|
||||
|
||||
|
|
|
@ -53,12 +53,13 @@ export const extractForecastSeriesContexts = (
|
|||
|
||||
export const extractForecastValuesFromTooltipParams = (
|
||||
params: any[],
|
||||
isHorizontal = false,
|
||||
): Record<string, ForecastValue> => {
|
||||
const values: Record<string, ForecastValue> = {};
|
||||
params.forEach(param => {
|
||||
const { marker, seriesId, value } = param;
|
||||
const context = extractForecastSeriesContext(seriesId);
|
||||
const numericValue = (value as [Date, number])[1];
|
||||
const numericValue = isHorizontal ? value[0] : value[1];
|
||||
if (numericValue) {
|
||||
if (!(context.name in values))
|
||||
values[context.name] = {
|
||||
|
|
|
@ -42,9 +42,15 @@ export function extractSeries(
|
|||
fillNeighborValue?: number;
|
||||
xAxis?: string;
|
||||
removeNulls?: boolean;
|
||||
isHorizontal?: boolean;
|
||||
} = {},
|
||||
): SeriesOption[] {
|
||||
const { fillNeighborValue, xAxis = DTTM_ALIAS, removeNulls = false } = opts;
|
||||
const {
|
||||
fillNeighborValue,
|
||||
xAxis = DTTM_ALIAS,
|
||||
removeNulls = false,
|
||||
isHorizontal = false,
|
||||
} = opts;
|
||||
if (data.length === 0) return [];
|
||||
const rows: DataRecord[] = data.map(datum => ({
|
||||
...datum,
|
||||
|
@ -69,7 +75,8 @@ export function extractSeries(
|
|||
: row[key],
|
||||
];
|
||||
})
|
||||
.filter(obs => !removeNulls || (obs[0] !== null && obs[1] !== null)),
|
||||
.filter(obs => !removeNulls || (obs[0] !== null && obs[1] !== null))
|
||||
.map(obs => (isHorizontal ? [obs[1], obs[0]] : obs)),
|
||||
}));
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue