mirror of
https://github.com/apache/superset.git
synced 2024-09-19 20:19:37 -04:00
feat: implement labelFlush behavior for continuous axes (#117)
* feat: add labelFlush to definition * feat: implement flushing
This commit is contained in:
parent
2333030abf
commit
c691415702
@ -1,6 +1,7 @@
|
|||||||
import { LineChartPlugin as LegacyLineChartPlugin } from '../../../../../superset-ui-preset-chart-xy/src/legacy';
|
import { LineChartPlugin as LegacyLineChartPlugin } from '../../../../../superset-ui-preset-chart-xy/src/legacy';
|
||||||
import { LineChartPlugin } from '../../../../../superset-ui-preset-chart-xy/src';
|
import { LineChartPlugin } from '../../../../../superset-ui-preset-chart-xy/src';
|
||||||
import BasicStories from './stories/basic';
|
import BasicStories from './stories/basic';
|
||||||
|
import FlushStories from './stories/flush';
|
||||||
import QueryStories from './stories/query';
|
import QueryStories from './stories/query';
|
||||||
import LegacyStories from './stories/legacy';
|
import LegacyStories from './stories/legacy';
|
||||||
import MissingStories from './stories/missing';
|
import MissingStories from './stories/missing';
|
||||||
@ -13,6 +14,7 @@ new LineChartPlugin().configure({ key: LINE_PLUGIN_TYPE }).register();
|
|||||||
export default {
|
export default {
|
||||||
examples: [
|
examples: [
|
||||||
...BasicStories,
|
...BasicStories,
|
||||||
|
...FlushStories,
|
||||||
...MissingStories,
|
...MissingStories,
|
||||||
...TimeShiftStories,
|
...TimeShiftStories,
|
||||||
...LegacyStories,
|
...LegacyStories,
|
||||||
|
@ -0,0 +1,136 @@
|
|||||||
|
/* eslint-disable no-magic-numbers, sort-keys */
|
||||||
|
import * as React from 'react';
|
||||||
|
import { SuperChart, ChartProps } from '@superset-ui/chart';
|
||||||
|
import { radios } from '@storybook/addon-knobs';
|
||||||
|
import rawData from '../data/data';
|
||||||
|
import { LINE_PLUGIN_TYPE } from '../constants';
|
||||||
|
|
||||||
|
const MIN_TIME = new Date(Date.UTC(1980, 0, 1)).getTime();
|
||||||
|
const MAX_TIME = new Date(Date.UTC(2000, 1, 1)).getTime();
|
||||||
|
const data = rawData.filter(({ x }) => x >= MIN_TIME && x <= MAX_TIME);
|
||||||
|
|
||||||
|
export default [
|
||||||
|
{
|
||||||
|
renderStory: () => [
|
||||||
|
<SuperChart
|
||||||
|
key="line1"
|
||||||
|
chartType={LINE_PLUGIN_TYPE}
|
||||||
|
chartProps={
|
||||||
|
new ChartProps({
|
||||||
|
datasource: { verboseMap: {} },
|
||||||
|
formData: {
|
||||||
|
encoding: {
|
||||||
|
x: {
|
||||||
|
field: 'x',
|
||||||
|
type: 'temporal',
|
||||||
|
format: '%Y',
|
||||||
|
scale: {
|
||||||
|
type: 'utc',
|
||||||
|
},
|
||||||
|
axis: {
|
||||||
|
tickCount: 6,
|
||||||
|
orient: radios('x.axis.orient', { top: 'top', bottom: 'bottom' }, 'bottom'),
|
||||||
|
title: radios(
|
||||||
|
'x.axis.title',
|
||||||
|
{ enable: 'Time', disable: '', '': undefined },
|
||||||
|
'Time',
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
field: 'y',
|
||||||
|
type: 'quantitative',
|
||||||
|
scale: {
|
||||||
|
type: 'linear',
|
||||||
|
},
|
||||||
|
axis: {
|
||||||
|
tickCount: 3,
|
||||||
|
orient: radios(
|
||||||
|
'y.axis.orient',
|
||||||
|
{ left: 'left', right: 'right', '': undefined },
|
||||||
|
'left',
|
||||||
|
),
|
||||||
|
title: radios(
|
||||||
|
'y.axis.title',
|
||||||
|
{ enable: 'Score', disable: '', '': undefined },
|
||||||
|
'Score',
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
stroke: {
|
||||||
|
field: 'name',
|
||||||
|
type: 'nominal',
|
||||||
|
legend: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
height: 200,
|
||||||
|
payload: { data },
|
||||||
|
width: 400,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>,
|
||||||
|
<SuperChart
|
||||||
|
key="line1"
|
||||||
|
chartType={LINE_PLUGIN_TYPE}
|
||||||
|
chartProps={
|
||||||
|
new ChartProps({
|
||||||
|
datasource: { verboseMap: {} },
|
||||||
|
formData: {
|
||||||
|
encoding: {
|
||||||
|
x: {
|
||||||
|
field: 'x',
|
||||||
|
type: 'temporal',
|
||||||
|
format: '%Y',
|
||||||
|
scale: {
|
||||||
|
type: 'utc',
|
||||||
|
},
|
||||||
|
axis: {
|
||||||
|
labelFlush: 5,
|
||||||
|
tickCount: 6,
|
||||||
|
orient: radios('x.axis.orient', { top: 'top', bottom: 'bottom' }, 'bottom'),
|
||||||
|
title: radios(
|
||||||
|
'x.axis.title',
|
||||||
|
{ enable: 'Time', disable: '', '': undefined },
|
||||||
|
'Time',
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
field: 'y',
|
||||||
|
type: 'quantitative',
|
||||||
|
scale: {
|
||||||
|
type: 'linear',
|
||||||
|
},
|
||||||
|
axis: {
|
||||||
|
tickCount: 3,
|
||||||
|
orient: radios(
|
||||||
|
'y.axis.orient',
|
||||||
|
{ left: 'left', right: 'right', '': undefined },
|
||||||
|
'left',
|
||||||
|
),
|
||||||
|
title: radios(
|
||||||
|
'y.axis.title',
|
||||||
|
{ enable: 'Score', disable: '', '': undefined },
|
||||||
|
'Score',
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
stroke: {
|
||||||
|
field: 'name',
|
||||||
|
type: 'nominal',
|
||||||
|
legend: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
height: 200,
|
||||||
|
payload: { data },
|
||||||
|
width: 400,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
/>,
|
||||||
|
],
|
||||||
|
storyName: 'with labelFlush',
|
||||||
|
storyPath: 'preset-chart-xy|LineChartPlugin',
|
||||||
|
},
|
||||||
|
];
|
@ -1,7 +1,7 @@
|
|||||||
/* eslint-disable no-magic-numbers */
|
/* eslint-disable no-magic-numbers */
|
||||||
import { CSSProperties } from 'react';
|
import { CSSProperties } from 'react';
|
||||||
import { Value } from 'vega-lite/build/src/channeldef';
|
import { Value } from 'vega-lite/build/src/channeldef';
|
||||||
import { getTextDimension, Margin } from '@superset-ui/dimension';
|
import { getTextDimension, Margin, Dimension } from '@superset-ui/dimension';
|
||||||
import { CategoricalColorScale } from '@superset-ui/color';
|
import { CategoricalColorScale } from '@superset-ui/color';
|
||||||
import { extractFormatFromTypeAndFormat } from './parsers/extractFormat';
|
import { extractFormatFromTypeAndFormat } from './parsers/extractFormat';
|
||||||
import { CoreAxis, LabelOverlapStrategy, AxisOrient } from './types/Axis';
|
import { CoreAxis, LabelOverlapStrategy, AxisOrient } from './types/Axis';
|
||||||
@ -31,6 +31,19 @@ const DEFAULT_Y_CONFIG: CoreAxis = {
|
|||||||
orient: 'left',
|
orient: 'left',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export interface AxisLayout {
|
||||||
|
axisWidth: number;
|
||||||
|
labelAngle: number;
|
||||||
|
labelFlush: number | boolean;
|
||||||
|
labelOffset: number;
|
||||||
|
labelOverlap: 'flat' | 'rotate';
|
||||||
|
minMargin: Partial<Margin>;
|
||||||
|
orient: AxisOrient;
|
||||||
|
tickLabelDimensions: Dimension[];
|
||||||
|
tickLabels: string[];
|
||||||
|
tickTextAnchor?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export default class AxisAgent<Def extends ChannelDef<Output>, Output extends Value = Value> {
|
export default class AxisAgent<Def extends ChannelDef<Output>, Output extends Value = Value> {
|
||||||
private readonly channelEncoder: ChannelEncoder<Def, Output>;
|
private readonly channelEncoder: ChannelEncoder<Def, Output>;
|
||||||
private readonly format?: (value: any) => string;
|
private readonly format?: (value: any) => string;
|
||||||
@ -108,17 +121,10 @@ export default class AxisAgent<Def extends ChannelDef<Output>, Output extends Va
|
|||||||
labelAngle?: number;
|
labelAngle?: number;
|
||||||
tickLength?: number;
|
tickLength?: number;
|
||||||
tickTextStyle?: CSSProperties;
|
tickTextStyle?: CSSProperties;
|
||||||
}): {
|
}): AxisLayout {
|
||||||
labelAngle: number;
|
|
||||||
labelOffset: number;
|
|
||||||
labelOverlap: 'flat' | 'rotate';
|
|
||||||
minMargin: Partial<Margin>;
|
|
||||||
orient: AxisOrient;
|
|
||||||
tickTextAnchor?: string;
|
|
||||||
} {
|
|
||||||
const tickLabels = this.getTickLabels();
|
const tickLabels = this.getTickLabels();
|
||||||
|
|
||||||
const labelDimensions = tickLabels.map((text: string) =>
|
const tickLabelDimensions = tickLabels.map((text: string) =>
|
||||||
getTextDimension({
|
getTextDimension({
|
||||||
style: tickTextStyle,
|
style: tickTextStyle,
|
||||||
text,
|
text,
|
||||||
@ -127,7 +133,7 @@ export default class AxisAgent<Def extends ChannelDef<Output>, Output extends Va
|
|||||||
|
|
||||||
const { labelOverlap, labelPadding, orient } = this.config;
|
const { labelOverlap, labelPadding, orient } = this.config;
|
||||||
|
|
||||||
const maxWidth = Math.max(...labelDimensions.map(d => d.width), 0);
|
const maxWidth = Math.max(...tickLabelDimensions.map(d => d.width), 0);
|
||||||
|
|
||||||
// TODO: Add other strategies: stagger, chop, wrap.
|
// TODO: Add other strategies: stagger, chop, wrap.
|
||||||
let strategyForLabelOverlap = labelOverlap;
|
let strategyForLabelOverlap = labelOverlap;
|
||||||
@ -149,7 +155,7 @@ export default class AxisAgent<Def extends ChannelDef<Output>, Output extends Va
|
|||||||
|
|
||||||
if (this.channelEncoder.isX()) {
|
if (this.channelEncoder.isX()) {
|
||||||
if (strategyForLabelOverlap === 'flat') {
|
if (strategyForLabelOverlap === 'flat') {
|
||||||
const labelHeight = labelDimensions.length > 0 ? labelDimensions[0].height : 0;
|
const labelHeight = tickLabelDimensions.length > 0 ? tickLabelDimensions[0].height : 0;
|
||||||
labelOffset = labelHeight + labelPadding;
|
labelOffset = labelHeight + labelPadding;
|
||||||
requiredMargin += labelHeight;
|
requiredMargin += labelHeight;
|
||||||
} else if (strategyForLabelOverlap === 'rotate') {
|
} else if (strategyForLabelOverlap === 'rotate') {
|
||||||
@ -168,13 +174,21 @@ export default class AxisAgent<Def extends ChannelDef<Output>, Output extends Va
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
axisWidth,
|
||||||
labelAngle: strategyForLabelOverlap === 'flat' ? 0 : labelAngle,
|
labelAngle: strategyForLabelOverlap === 'flat' ? 0 : labelAngle,
|
||||||
|
labelFlush:
|
||||||
|
typeof this.config.labelFlush === 'undefined'
|
||||||
|
? // If not set, only enable flushing for continuous scales
|
||||||
|
this.channelEncoder.scale!.scaleTypeCategory === 'continuous'
|
||||||
|
: this.config.labelFlush,
|
||||||
labelOffset,
|
labelOffset,
|
||||||
labelOverlap: strategyForLabelOverlap,
|
labelOverlap: strategyForLabelOverlap,
|
||||||
minMargin: {
|
minMargin: {
|
||||||
[orient]: Math.ceil(requiredMargin),
|
[orient]: Math.ceil(requiredMargin),
|
||||||
},
|
},
|
||||||
orient,
|
orient,
|
||||||
|
tickLabelDimensions,
|
||||||
|
tickLabels,
|
||||||
tickTextAnchor,
|
tickTextAnchor,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -48,6 +48,7 @@ export interface ScaleAgent<Output extends Value> {
|
|||||||
| ScaleOrdinal<{ toString(): string }, Output>
|
| ScaleOrdinal<{ toString(): string }, Output>
|
||||||
| ScalePoint<{ toString(): string }>
|
| ScalePoint<{ toString(): string }>
|
||||||
| ScaleBand<{ toString(): string }>;
|
| ScaleBand<{ toString(): string }>;
|
||||||
|
scaleTypeCategory: 'continuous' | 'discrete' | 'discretizing';
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ScaleTypeToD3ScaleType<Output> {
|
export interface ScaleTypeToD3ScaleType<Output> {
|
||||||
@ -199,11 +200,26 @@ function createScale<Output extends Value>(
|
|||||||
return scale;
|
return scale;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const continuousScaleTypes = new Set(['linear', 'pow', 'sqrt', 'symlog', 'log', 'time', 'utc']);
|
||||||
|
const discreteScaleTypes = new Set(['band', 'point']);
|
||||||
|
const discretizingScaleTypes = new Set(['bin-ordinal', 'quantile', 'quantize', 'threshold']);
|
||||||
|
|
||||||
|
function getScaleTypeCategory(scaleType: ScaleType) {
|
||||||
|
if (continuousScaleTypes.has(scaleType)) {
|
||||||
|
return 'continuous';
|
||||||
|
}
|
||||||
|
if (discreteScaleTypes.has(scaleType)) {
|
||||||
|
return 'discrete';
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'discretizing';
|
||||||
|
}
|
||||||
|
|
||||||
export default function extractScale<Output extends Value>(
|
export default function extractScale<Output extends Value>(
|
||||||
channelType: ChannelType,
|
channelType: ChannelType,
|
||||||
definition: ChannelDef<Output>,
|
definition: ChannelDef<Output>,
|
||||||
namespace?: string,
|
namespace?: string,
|
||||||
) {
|
): ScaleAgent<Output> | undefined {
|
||||||
if (isNonValueDef(definition)) {
|
if (isNonValueDef(definition)) {
|
||||||
const scaleConfig =
|
const scaleConfig =
|
||||||
'scale' in definition && typeof definition.scale !== 'undefined' ? definition.scale : {};
|
'scale' in definition && typeof definition.scale !== 'undefined' ? definition.scale : {};
|
||||||
@ -240,6 +256,7 @@ export default function extractScale<Output extends Value>(
|
|||||||
value: number | string | boolean | null | undefined | Date,
|
value: number | string | boolean | null | undefined | Date,
|
||||||
) => Output,
|
) => Output,
|
||||||
scale,
|
scale,
|
||||||
|
scaleTypeCategory: getScaleTypeCategory(scaleType),
|
||||||
setDomain,
|
setDomain,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
/** See https://vega.github.io/vega-lite/docs/axis.html */
|
||||||
|
|
||||||
import { DateTime } from 'vega-lite/build/src/datetime';
|
import { DateTime } from 'vega-lite/build/src/datetime';
|
||||||
|
|
||||||
export type AxisOrient = 'top' | 'bottom' | 'left' | 'right';
|
export type AxisOrient = 'top' | 'bottom' | 'left' | 'right';
|
||||||
@ -7,6 +9,15 @@ export type LabelOverlapStrategy = 'auto' | 'flat' | 'rotate';
|
|||||||
export interface CoreAxis {
|
export interface CoreAxis {
|
||||||
format?: string;
|
format?: string;
|
||||||
labelAngle: number;
|
labelAngle: number;
|
||||||
|
/**
|
||||||
|
* Indicates if the first and last axis labels should be aligned flush with the scale range.
|
||||||
|
* Flush alignment for a horizontal axis will left-align the first label and right-align the last label.
|
||||||
|
* For vertical axes, bottom and top text baselines are applied instead.
|
||||||
|
* If this property is a number, it also indicates the number of pixels by which to offset the first and last labels;
|
||||||
|
* for example, a value of 2 will flush-align the first and last labels
|
||||||
|
* and also push them 2 pixels outward from the center of the axis.
|
||||||
|
* The additional adjustment can sometimes help the labels better visually group with corresponding axis ticks. */
|
||||||
|
labelFlush?: boolean | number;
|
||||||
labelOverlap: LabelOverlapStrategy;
|
labelOverlap: LabelOverlapStrategy;
|
||||||
/** The padding, in pixels, between axis and text labels. */
|
/** The padding, in pixels, between axis and text labels. */
|
||||||
labelPadding: number;
|
labelPadding: number;
|
||||||
|
@ -8,11 +8,11 @@ import { Margin, mergeMargin, Dimension } from '@superset-ui/dimension';
|
|||||||
import { ChartFrame } from '@superset-ui/chart-composition';
|
import { ChartFrame } from '@superset-ui/chart-composition';
|
||||||
import createTickComponent from './createTickComponent';
|
import createTickComponent from './createTickComponent';
|
||||||
import ChannelEncoder from '../encodeable/ChannelEncoder';
|
import ChannelEncoder from '../encodeable/ChannelEncoder';
|
||||||
import { AxisOrient } from '../encodeable/types/Axis';
|
|
||||||
import { XFieldDef, YFieldDef } from '../encodeable/types/ChannelDef';
|
import { XFieldDef, YFieldDef } from '../encodeable/types/ChannelDef';
|
||||||
import { PlainObject } from '../encodeable/types/Data';
|
import { PlainObject } from '../encodeable/types/Data';
|
||||||
import { DEFAULT_LABEL_ANGLE } from './constants';
|
import { DEFAULT_LABEL_ANGLE } from './constants';
|
||||||
import convertScaleToDataUIScale from './convertScaleToDataUIScaleShape';
|
import convertScaleToDataUIScale from './convertScaleToDataUIScaleShape';
|
||||||
|
import { AxisLayout } from '../encodeable/AxisAgent';
|
||||||
|
|
||||||
// Additional margin to avoid content hidden behind scroll bar
|
// Additional margin to avoid content hidden behind scroll bar
|
||||||
const OVERFLOW_MARGIN = 8;
|
const OVERFLOW_MARGIN = 8;
|
||||||
@ -37,20 +37,9 @@ export default class XYChartLayout {
|
|||||||
margin: Margin;
|
margin: Margin;
|
||||||
config: XYChartLayoutConfig;
|
config: XYChartLayoutConfig;
|
||||||
|
|
||||||
xLayout?: {
|
xLayout?: AxisLayout;
|
||||||
labelOffset: number;
|
|
||||||
labelOverlap: string;
|
|
||||||
labelAngle: number;
|
|
||||||
tickTextAnchor?: string;
|
|
||||||
minMargin: Partial<Margin>;
|
|
||||||
orient: AxisOrient;
|
|
||||||
};
|
|
||||||
|
|
||||||
yLayout?: {
|
yLayout?: AxisLayout;
|
||||||
labelOffset: number;
|
|
||||||
minMargin: Partial<Margin>;
|
|
||||||
orient: AxisOrient;
|
|
||||||
};
|
|
||||||
|
|
||||||
// eslint-disable-next-line complexity
|
// eslint-disable-next-line complexity
|
||||||
constructor(config: XYChartLayoutConfig) {
|
constructor(config: XYChartLayoutConfig) {
|
||||||
|
@ -1,18 +1,19 @@
|
|||||||
/* eslint-disable no-magic-numbers */
|
/* eslint-disable no-magic-numbers */
|
||||||
|
|
||||||
import React, { CSSProperties } from 'react';
|
import React, { CSSProperties } from 'react';
|
||||||
|
import { Dimension } from '@superset-ui/dimension';
|
||||||
|
import { AxisLayout } from '../encodeable/AxisAgent';
|
||||||
|
|
||||||
export default function createTickComponent({
|
export default function createTickComponent({
|
||||||
|
axisWidth,
|
||||||
labelAngle,
|
labelAngle,
|
||||||
|
labelFlush,
|
||||||
labelOverlap,
|
labelOverlap,
|
||||||
orient,
|
orient,
|
||||||
tickTextAnchor = 'start',
|
tickLabels,
|
||||||
}: {
|
tickLabelDimensions,
|
||||||
labelAngle: number;
|
tickTextAnchor = 'middle',
|
||||||
labelOverlap: string;
|
}: AxisLayout) {
|
||||||
orient: string;
|
|
||||||
tickTextAnchor?: string;
|
|
||||||
}) {
|
|
||||||
if (labelOverlap === 'rotate' && labelAngle !== 0) {
|
if (labelOverlap === 'rotate' && labelAngle !== 0) {
|
||||||
let xOffset = labelAngle > 0 ? -6 : 6;
|
let xOffset = labelAngle > 0 ? -6 : 6;
|
||||||
if (orient === 'top') {
|
if (orient === 'top') {
|
||||||
@ -20,7 +21,7 @@ export default function createTickComponent({
|
|||||||
}
|
}
|
||||||
const yOffset = orient === 'top' ? -3 : 0;
|
const yOffset = orient === 'top' ? -3 : 0;
|
||||||
|
|
||||||
const TickComponent = ({
|
return ({
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
dy,
|
dy,
|
||||||
@ -39,8 +40,50 @@ export default function createTickComponent({
|
|||||||
</text>
|
</text>
|
||||||
</g>
|
</g>
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return TickComponent;
|
if (labelFlush === true || typeof labelFlush === 'number') {
|
||||||
|
const labelToDimensionMap = new Map<string, Dimension>();
|
||||||
|
tickLabels.forEach((label, i) => {
|
||||||
|
labelToDimensionMap.set(label, tickLabelDimensions[i]);
|
||||||
|
});
|
||||||
|
|
||||||
|
return ({
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
dy,
|
||||||
|
formattedValue = '',
|
||||||
|
...textStyle
|
||||||
|
}: {
|
||||||
|
x: number;
|
||||||
|
y: number;
|
||||||
|
dy?: number;
|
||||||
|
formattedValue: string;
|
||||||
|
textStyle: CSSProperties;
|
||||||
|
}) => {
|
||||||
|
const dimension = labelToDimensionMap.get(formattedValue);
|
||||||
|
const labelWidth = typeof dimension === 'undefined' ? 0 : dimension.width;
|
||||||
|
let textAnchor = tickTextAnchor;
|
||||||
|
let xOffset = 0;
|
||||||
|
|
||||||
|
if (x - labelWidth / 2 < 0) {
|
||||||
|
textAnchor = 'start';
|
||||||
|
if (typeof labelFlush === 'number') {
|
||||||
|
xOffset -= labelFlush;
|
||||||
|
}
|
||||||
|
} else if (x + labelWidth / 2 > axisWidth) {
|
||||||
|
textAnchor = 'end';
|
||||||
|
if (typeof labelFlush === 'number') {
|
||||||
|
xOffset += labelFlush;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<text x={x + xOffset} y={y} {...textStyle} textAnchor={textAnchor}>
|
||||||
|
{formattedValue}
|
||||||
|
</text>
|
||||||
|
);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// This will render the tick as horizontal string.
|
// This will render the tick as horizontal string.
|
||||||
|
Loading…
Reference in New Issue
Block a user