mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 17:59:23 -04:00
[Gauge] Vis editors gauge legacy percent mode. (#126318)
* Added legacy percentage mode to the gauge. Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
5ad355e8c7
commit
720fbed521
13 changed files with 170 additions and 60 deletions
|
@ -47,6 +47,7 @@ Object {
|
|||
"metric": "col-0-1",
|
||||
"min": "col-1-2",
|
||||
"palette": undefined,
|
||||
"percentageMode": false,
|
||||
"shape": "arc",
|
||||
"ticksPosition": "auto",
|
||||
},
|
||||
|
@ -96,6 +97,7 @@ Object {
|
|||
"metric": "col-0-1",
|
||||
"min": "col-1-2",
|
||||
"palette": undefined,
|
||||
"percentageMode": false,
|
||||
"shape": "arc",
|
||||
"ticksPosition": "auto",
|
||||
},
|
||||
|
@ -143,6 +145,7 @@ Object {
|
|||
"metric": "col-0-1",
|
||||
"min": "col-1-2",
|
||||
"palette": undefined,
|
||||
"percentageMode": false,
|
||||
"shape": "horizontalBullet",
|
||||
"ticksPosition": "auto",
|
||||
},
|
||||
|
@ -190,6 +193,7 @@ Object {
|
|||
"metric": "col-0-1",
|
||||
"min": "col-1-2",
|
||||
"palette": undefined,
|
||||
"percentageMode": false,
|
||||
"shape": "horizontalBullet",
|
||||
"ticksPosition": "auto",
|
||||
},
|
||||
|
@ -237,6 +241,7 @@ Object {
|
|||
"metric": "col-0-1",
|
||||
"min": "col-1-2",
|
||||
"palette": undefined,
|
||||
"percentageMode": false,
|
||||
"shape": "horizontalBullet",
|
||||
"ticksPosition": "bands",
|
||||
},
|
||||
|
@ -286,6 +291,7 @@ Object {
|
|||
"metric": "col-0-1",
|
||||
"min": "col-1-2",
|
||||
"palette": undefined,
|
||||
"percentageMode": false,
|
||||
"shape": "circle",
|
||||
"ticksPosition": "auto",
|
||||
},
|
||||
|
@ -335,6 +341,7 @@ Object {
|
|||
"metric": "col-0-1",
|
||||
"min": "col-1-2",
|
||||
"palette": undefined,
|
||||
"percentageMode": false,
|
||||
"shape": "circle",
|
||||
"ticksPosition": "auto",
|
||||
},
|
||||
|
@ -382,6 +389,7 @@ Object {
|
|||
"metric": "col-0-1",
|
||||
"min": "col-1-2",
|
||||
"palette": undefined,
|
||||
"percentageMode": false,
|
||||
"shape": "horizontalBullet",
|
||||
"ticksPosition": "auto",
|
||||
},
|
||||
|
@ -429,6 +437,7 @@ Object {
|
|||
"metric": "col-0-1",
|
||||
"min": "col-1-2",
|
||||
"palette": undefined,
|
||||
"percentageMode": false,
|
||||
"shape": "horizontalBullet",
|
||||
"ticksPosition": "hidden",
|
||||
},
|
||||
|
@ -476,6 +485,7 @@ Object {
|
|||
"metric": "col-0-1",
|
||||
"min": "col-1-2",
|
||||
"palette": undefined,
|
||||
"percentageMode": false,
|
||||
"shape": "horizontalBullet",
|
||||
"ticksPosition": "auto",
|
||||
},
|
||||
|
@ -523,6 +533,7 @@ Object {
|
|||
"metric": "col-0-1",
|
||||
"min": "col-1-2",
|
||||
"palette": undefined,
|
||||
"percentageMode": false,
|
||||
"shape": "horizontalBullet",
|
||||
"ticksPosition": "auto",
|
||||
},
|
||||
|
@ -570,6 +581,7 @@ Object {
|
|||
"metric": "col-0-1",
|
||||
"min": "col-1-2",
|
||||
"palette": undefined,
|
||||
"percentageMode": false,
|
||||
"shape": "horizontalBullet",
|
||||
"ticksPosition": "auto",
|
||||
},
|
||||
|
@ -617,6 +629,7 @@ Object {
|
|||
"metric": "col-0-1",
|
||||
"min": "col-1-2",
|
||||
"palette": undefined,
|
||||
"percentageMode": false,
|
||||
"shape": "horizontalBullet",
|
||||
"ticksPosition": "auto",
|
||||
},
|
||||
|
@ -664,6 +677,7 @@ Object {
|
|||
"metric": "col-0-1",
|
||||
"min": "col-1-2",
|
||||
"palette": undefined,
|
||||
"percentageMode": false,
|
||||
"shape": "verticalBullet",
|
||||
"ticksPosition": "auto",
|
||||
},
|
||||
|
|
|
@ -180,6 +180,14 @@ export const gaugeFunction = (): GaugeExpressionFunctionDefinition => ({
|
|||
defaultMessage: 'Specifies the mode of centralMajor',
|
||||
}),
|
||||
},
|
||||
// used only in legacy gauge, consider it as @deprecated
|
||||
percentageMode: {
|
||||
types: ['boolean'],
|
||||
default: false,
|
||||
help: i18n.translate('expressionGauge.functions.gauge.percentageMode.help', {
|
||||
defaultMessage: 'Enables relative precentage mode',
|
||||
}),
|
||||
},
|
||||
ariaLabel: {
|
||||
types: ['string'],
|
||||
help: i18n.translate('expressionGauge.functions.gaugeChart.config.ariaLabel.help', {
|
||||
|
|
|
@ -45,6 +45,8 @@ export interface GaugeState {
|
|||
colorMode?: GaugeColorMode;
|
||||
palette?: PaletteOutput<CustomPaletteParams>;
|
||||
shape: GaugeShape;
|
||||
/** @deprecated This field is deprecated and going to be removed in the futher release versions. */
|
||||
percentageMode?: boolean;
|
||||
}
|
||||
|
||||
export type GaugeArguments = GaugeState & {
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import type { ChartsPluginSetup } from '../../../../charts/public';
|
||||
import type { ChartsPluginSetup, PaletteRegistry } from '../../../../charts/public';
|
||||
import type { IFieldFormat, SerializedFieldFormat } from '../../../../field_formats/common';
|
||||
import type { GaugeExpressionProps } from './expression_functions';
|
||||
|
||||
|
@ -15,6 +15,7 @@ export type FormatFactory = (mapping?: SerializedFieldFormat) => IFieldFormat;
|
|||
export type GaugeRenderProps = GaugeExpressionProps & {
|
||||
formatFactory: FormatFactory;
|
||||
chartsThemeService: ChartsPluginSetup['theme'];
|
||||
paletteService: PaletteRegistry;
|
||||
};
|
||||
|
||||
export interface ColorStop {
|
||||
|
|
|
@ -41,6 +41,7 @@ exports[`GaugeComponent renders the chart 1`] = `
|
|||
4,
|
||||
]
|
||||
}
|
||||
tooltipValueFormatter={[Function]}
|
||||
/>
|
||||
</Chart>
|
||||
`;
|
||||
|
|
|
@ -54,6 +54,7 @@ jest.mock('@elastic/charts', () => {
|
|||
});
|
||||
|
||||
const chartsThemeService = chartPluginMock.createSetupContract().theme;
|
||||
const paletteThemeService = chartPluginMock.createSetupContract().palettes;
|
||||
const formatService = fieldFormatsServiceMock.createStartContract();
|
||||
const args: GaugeArguments = {
|
||||
labelMajor: 'Gauge',
|
||||
|
@ -81,12 +82,13 @@ const createData = (
|
|||
describe('GaugeComponent', function () {
|
||||
let wrapperProps: GaugeRenderProps;
|
||||
|
||||
beforeAll(() => {
|
||||
beforeAll(async () => {
|
||||
wrapperProps = {
|
||||
data: createData(),
|
||||
chartsThemeService,
|
||||
args,
|
||||
formatFactory: formatService.deserialize,
|
||||
paletteService: await paletteThemeService.getPalettes(),
|
||||
};
|
||||
});
|
||||
|
||||
|
|
|
@ -5,10 +5,10 @@
|
|||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
import React, { FC, memo } from 'react';
|
||||
import React, { FC, memo, useCallback } from 'react';
|
||||
import { Chart, Goal, Settings } from '@elastic/charts';
|
||||
import { FormattedMessage } from '@kbn/i18n-react';
|
||||
import type { CustomPaletteState } from '../../../../charts/public';
|
||||
import type { CustomPaletteState, PaletteOutput } from '../../../../charts/public';
|
||||
import { EmptyPlaceholder } from '../../../../charts/public';
|
||||
import { isVisDimension } from '../../../../visualizations/common/utils';
|
||||
import {
|
||||
|
@ -42,37 +42,7 @@ declare global {
|
|||
}
|
||||
}
|
||||
|
||||
function normalizeColors(
|
||||
{ colors, stops, range, rangeMin, rangeMax }: CustomPaletteState,
|
||||
min: number,
|
||||
max: number
|
||||
) {
|
||||
if (!colors) {
|
||||
return;
|
||||
}
|
||||
const colorsOutOfRangeSmaller = Math.max(
|
||||
stops.filter((stop, i) => (range === 'percent' ? stop < 0 : stop < min)).length,
|
||||
0
|
||||
);
|
||||
let updatedColors = colors.slice(colorsOutOfRangeSmaller);
|
||||
|
||||
let correctMin = rangeMin;
|
||||
let correctMax = rangeMax;
|
||||
if (range === 'percent') {
|
||||
correctMin = min + rangeMin * ((max - min) / 100);
|
||||
correctMax = min + rangeMax * ((max - min) / 100);
|
||||
}
|
||||
|
||||
if (correctMin > min && isFinite(correctMin)) {
|
||||
updatedColors = [`rgba(255,255,255,0)`, ...updatedColors];
|
||||
}
|
||||
|
||||
if (correctMax < max && isFinite(correctMax)) {
|
||||
updatedColors = [...updatedColors, `rgba(255,255,255,0)`];
|
||||
}
|
||||
|
||||
return updatedColors;
|
||||
}
|
||||
const TRANSPARENT = `rgba(255,255,255,0)`;
|
||||
|
||||
function normalizeBands(
|
||||
{ colors, stops, range, rangeMax, rangeMin }: CustomPaletteState,
|
||||
|
@ -111,6 +81,28 @@ function normalizeBands(
|
|||
return [...firstRanges, ...orderedStops, ...lastRanges];
|
||||
}
|
||||
|
||||
const toPercents = (min: number, max: number) => (v: number) => (v - min) / (max - min);
|
||||
|
||||
function normalizeBandsLegacy({ colors, stops }: CustomPaletteState, value: number) {
|
||||
const min = stops[0];
|
||||
const max = stops[stops.length - 1];
|
||||
const convertToPercents = toPercents(min, max);
|
||||
const normalizedStops = stops.map(convertToPercents);
|
||||
|
||||
if (max < value) {
|
||||
normalizedStops.push(convertToPercents(value));
|
||||
}
|
||||
|
||||
return normalizedStops;
|
||||
}
|
||||
|
||||
function actualValueToPercentsLegacy({ stops }: CustomPaletteState, value: number) {
|
||||
const min = stops[0];
|
||||
const max = stops[stops.length - 1];
|
||||
const convertToPercents = toPercents(min, max);
|
||||
return convertToPercents(value);
|
||||
}
|
||||
|
||||
function getTitle(
|
||||
majorMode?: GaugeLabelMajorMode | GaugeCentralMajorMode,
|
||||
major?: string,
|
||||
|
@ -144,7 +136,8 @@ function getTicksLabels(baseStops: number[]) {
|
|||
function getTicks(
|
||||
ticksPosition: GaugeTicksPosition,
|
||||
range: [number, number],
|
||||
colorBands?: number[]
|
||||
colorBands?: number[],
|
||||
percentageMode?: boolean
|
||||
) {
|
||||
if (ticksPosition === GaugeTicksPositions.HIDDEN) {
|
||||
return [];
|
||||
|
@ -158,16 +151,40 @@ function getTicks(
|
|||
const min = Math.min(...(colorBands || []), ...range);
|
||||
const max = Math.max(...(colorBands || []), ...range);
|
||||
const step = (max - min) / TICKS_NO;
|
||||
return [
|
||||
|
||||
const ticks = [
|
||||
...Array(TICKS_NO)
|
||||
.fill(null)
|
||||
.map((_, i) => Number((min + step * i).toFixed(2))),
|
||||
max,
|
||||
];
|
||||
const convertToPercents = toPercents(min, max);
|
||||
return percentageMode ? ticks.map(convertToPercents) : ticks;
|
||||
}
|
||||
|
||||
const calculateRealRangeValueMin = (
|
||||
relativeRangeValue: number,
|
||||
{ min, max }: { min: number; max: number }
|
||||
) => {
|
||||
if (isFinite(relativeRangeValue)) {
|
||||
return relativeRangeValue * ((max - min) / 100);
|
||||
}
|
||||
return min;
|
||||
};
|
||||
|
||||
const calculateRealRangeValueMax = (
|
||||
relativeRangeValue: number,
|
||||
{ min, max }: { min: number; max: number }
|
||||
) => {
|
||||
if (isFinite(relativeRangeValue)) {
|
||||
return relativeRangeValue * ((max - min) / 100);
|
||||
}
|
||||
|
||||
return max;
|
||||
};
|
||||
|
||||
export const GaugeComponent: FC<GaugeRenderProps> = memo(
|
||||
({ data, args, formatFactory, chartsThemeService }) => {
|
||||
({ data, args, formatFactory, paletteService, chartsThemeService }) => {
|
||||
const {
|
||||
shape: gaugeType,
|
||||
palette,
|
||||
|
@ -179,6 +196,40 @@ export const GaugeComponent: FC<GaugeRenderProps> = memo(
|
|||
centralMajorMode,
|
||||
ticksPosition,
|
||||
} = args;
|
||||
|
||||
const getColor = useCallback(
|
||||
(
|
||||
value,
|
||||
paletteConfig: PaletteOutput<CustomPaletteState>,
|
||||
bands: number[],
|
||||
percentageMode?: boolean
|
||||
) => {
|
||||
const { rangeMin, rangeMax, range }: CustomPaletteState = paletteConfig.params!;
|
||||
const minRealValue = bands[0];
|
||||
const maxRealValue = bands[bands.length - 1];
|
||||
let min = rangeMin;
|
||||
let max = rangeMax;
|
||||
|
||||
let stops = paletteConfig.params?.stops ?? [];
|
||||
|
||||
if (percentageMode) {
|
||||
stops = bands.map((v) => v * 100);
|
||||
}
|
||||
|
||||
if (range === 'percent') {
|
||||
const minMax = { min: minRealValue, max: maxRealValue };
|
||||
|
||||
min = calculateRealRangeValueMin(min, minMax);
|
||||
max = calculateRealRangeValueMax(max, minMax);
|
||||
}
|
||||
|
||||
return paletteService
|
||||
.get(paletteConfig?.name ?? 'custom')
|
||||
.getColorForValue?.(value, { ...paletteConfig.params, stops }, { min, max });
|
||||
},
|
||||
[paletteService]
|
||||
);
|
||||
|
||||
const table = data;
|
||||
const accessors = getAccessorsFromArgs(args, table.columns);
|
||||
|
||||
|
@ -251,18 +302,23 @@ export const GaugeComponent: FC<GaugeRenderProps> = memo(
|
|||
customMetricFormatParams ?? tableMetricFormatParams ?? defaultMetricFormatParams
|
||||
);
|
||||
|
||||
const colors = palette?.params?.colors ? normalizeColors(palette.params, min, max) : undefined;
|
||||
const bands: number[] = (palette?.params as CustomPaletteState)
|
||||
? normalizeBands(args.palette?.params as CustomPaletteState, { min, max })
|
||||
let bands: number[] = (palette?.params as CustomPaletteState)
|
||||
? normalizeBands(palette?.params as CustomPaletteState, { min, max })
|
||||
: [min, max];
|
||||
|
||||
// TODO: format in charts
|
||||
const formattedActual = Math.round(Math.min(Math.max(metricValue, min), max) * 1000) / 1000;
|
||||
const goalConfig = getGoalConfig(gaugeType);
|
||||
const totalTicks = getTicks(ticksPosition, [min, max], bands);
|
||||
let actualValue = Math.round(Math.min(Math.max(metricValue, min), max) * 1000) / 1000;
|
||||
const totalTicks = getTicks(ticksPosition, [min, max], bands, args.percentageMode);
|
||||
const ticks =
|
||||
gaugeType === GaugeShapes.CIRCLE ? totalTicks.slice(0, totalTicks.length - 1) : totalTicks;
|
||||
|
||||
if (args.percentageMode && palette?.params && palette?.params.stops?.length) {
|
||||
bands = normalizeBandsLegacy(palette?.params as CustomPaletteState, actualValue);
|
||||
actualValue = actualValueToPercentsLegacy(palette?.params as CustomPaletteState, actualValue);
|
||||
}
|
||||
|
||||
const goalConfig = getGoalConfig(gaugeType);
|
||||
|
||||
const labelMajorTitle = getTitle(labelMajorMode, labelMajor, metricColumn?.name);
|
||||
|
||||
// added extra space for nice rendering
|
||||
|
@ -271,7 +327,7 @@ export const GaugeComponent: FC<GaugeRenderProps> = memo(
|
|||
|
||||
const extraTitles = isRoundShape(gaugeType)
|
||||
? {
|
||||
centralMinor: tickFormatter.convert(metricValue),
|
||||
centralMinor: tickFormatter.convert(actualValue),
|
||||
centralMajor: getTitle(centralMajorMode, centralMajor, metricColumn?.name),
|
||||
}
|
||||
: {};
|
||||
|
@ -289,21 +345,27 @@ export const GaugeComponent: FC<GaugeRenderProps> = memo(
|
|||
subtype={getSubtypeByGaugeType(gaugeType)}
|
||||
base={bands[0]}
|
||||
target={goal && goal >= bands[0] && goal <= bands[bands.length - 1] ? goal : undefined}
|
||||
actual={formattedActual}
|
||||
actual={actualValue}
|
||||
tickValueFormatter={({ value: tickValue }) => tickFormatter.convert(tickValue)}
|
||||
tooltipValueFormatter={(tooltipValue) => tickFormatter.convert(tooltipValue)}
|
||||
bands={bands}
|
||||
ticks={ticks}
|
||||
bandFillColor={
|
||||
colorMode === GaugeColorModes.PALETTE && colors
|
||||
colorMode === GaugeColorModes.PALETTE
|
||||
? (val) => {
|
||||
const index = bands && bands.indexOf(val.value) - 1;
|
||||
return colors && index >= 0 && colors[index]
|
||||
? colors[index]
|
||||
: val.value <= bands[0]
|
||||
? colors[0]
|
||||
: colors[colors.length - 1];
|
||||
// bands value is equal to the stop. The purpose of this value is coloring the previous section, which is smaller, then the band.
|
||||
// So, the smaller value should be taken. For the first element -1, for the next - middle value of the previous section.
|
||||
let value = val.value - 1;
|
||||
const valueIndex = bands.indexOf(val.value);
|
||||
if (valueIndex > 0) {
|
||||
value = val.value - (bands[valueIndex] - bands[valueIndex - 1]) / 2;
|
||||
}
|
||||
|
||||
return args.palette
|
||||
? getColor(value, args.palette, bands, args.percentageMode) ?? TRANSPARENT
|
||||
: TRANSPARENT;
|
||||
}
|
||||
: () => `rgba(255,255,255,0)`
|
||||
: () => TRANSPARENT
|
||||
}
|
||||
labelMajor={labelMajorTitle ? `${labelMajorTitle}${majorExtraSpaces}` : labelMajorTitle}
|
||||
labelMinor={labelMinor ? `${labelMinor}${minorExtraSpaces}` : ''}
|
||||
|
|
|
@ -12,7 +12,7 @@ import { ThemeServiceStart } from '../../../../../core/public';
|
|||
import { KibanaThemeProvider } from '../../../../kibana_react/public';
|
||||
import { ExpressionRenderDefinition } from '../../../../expressions/common/expression_renderers';
|
||||
import { EXPRESSION_GAUGE_NAME, GaugeExpressionProps } from '../../common';
|
||||
import { getFormatService, getThemeService } from '../services';
|
||||
import { getFormatService, getPaletteService, getThemeService } from '../services';
|
||||
|
||||
interface ExpressionGaugeRendererDependencies {
|
||||
theme: ThemeServiceStart;
|
||||
|
@ -39,6 +39,7 @@ export const gaugeRenderer: (
|
|||
{...config}
|
||||
formatFactory={getFormatService().deserialize}
|
||||
chartsThemeService={getThemeService()}
|
||||
paletteService={getPaletteService()}
|
||||
/>
|
||||
</div>
|
||||
</KibanaThemeProvider>,
|
||||
|
|
|
@ -9,7 +9,7 @@ import { ChartsPluginSetup } from '../../../charts/public';
|
|||
import { CoreSetup, CoreStart } from '../../../../core/public';
|
||||
import { Plugin as ExpressionsPublicPlugin } from '../../../expressions/public';
|
||||
import { gaugeFunction } from '../common';
|
||||
import { setFormatService, setThemeService } from './services';
|
||||
import { setFormatService, setThemeService, setPaletteService } from './services';
|
||||
import { gaugeRenderer } from './expression_renderers';
|
||||
import type { FieldFormatsStart } from '../../../field_formats/public';
|
||||
|
||||
|
@ -28,6 +28,10 @@ export interface ExpressionGaugePluginStart {
|
|||
export class ExpressionGaugePlugin {
|
||||
public setup(core: CoreSetup, { expressions, charts }: ExpressionGaugePluginSetup) {
|
||||
setThemeService(charts.theme);
|
||||
charts.palettes.getPalettes().then((palettes) => {
|
||||
setPaletteService(palettes);
|
||||
});
|
||||
|
||||
expressions.registerFunction(gaugeFunction);
|
||||
expressions.registerRenderer(gaugeRenderer({ theme: core.theme }));
|
||||
}
|
||||
|
|
|
@ -7,4 +7,5 @@
|
|||
*/
|
||||
|
||||
export { getFormatService, setFormatService } from './format_service';
|
||||
export { setThemeService, getThemeService } from './theme_service';
|
||||
export { getThemeService, setThemeService } from './theme_service';
|
||||
export { getPaletteService, setPaletteService } from './palette_service';
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
import { createGetterSetter } from '../../../../kibana_utils/public';
|
||||
import { PaletteRegistry } from '../../../../charts/public';
|
||||
|
||||
export const [getPaletteService, setPaletteService] =
|
||||
createGetterSetter<PaletteRegistry>('palette');
|
|
@ -71,6 +71,7 @@ export const toExpressionAst: VisToExpressionAst<GaugeVisParams> = (vis, params)
|
|||
colorMode: 'palette',
|
||||
centralMajorMode,
|
||||
...(centralMajorMode === 'custom' ? { labelMinor: style.subText } : {}),
|
||||
percentageMode,
|
||||
});
|
||||
|
||||
if (colorsRange && colorsRange.length) {
|
||||
|
@ -80,8 +81,8 @@ export const toExpressionAst: VisToExpressionAst<GaugeVisParams> = (vis, params)
|
|||
range: percentageMode ? 'percent' : 'number',
|
||||
continuity: 'none',
|
||||
gradient: true,
|
||||
rangeMax: percentageMode ? 100 : Infinity,
|
||||
rangeMin: 0,
|
||||
rangeMax: percentageMode ? 100 : stopsWithColors.stop[stopsWithColors.stop.length - 1],
|
||||
rangeMin: stopsWithColors.stop[0],
|
||||
});
|
||||
|
||||
gauge.addArgument('palette', buildExpression([palette]));
|
||||
|
|
|
@ -34,7 +34,7 @@ export const getStopsWithColorsFromRanges = (
|
|||
) => {
|
||||
return ranges.reduce<PaletteConfig>(
|
||||
(acc, range, index, rangesArr) => {
|
||||
if (index && range.from !== rangesArr[index - 1].to) {
|
||||
if ((index && range.from !== rangesArr[index - 1].to) || index === 0) {
|
||||
acc.color.push(TRANSPARENT);
|
||||
acc.stop.push(range.from);
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue