diff --git a/src/__stories__/__data__/area/playground.ts b/src/__stories__/__data__/area/playground.ts index 6913d1898..fe369cd96 100644 --- a/src/__stories__/__data__/area/playground.ts +++ b/src/__stories__/__data__/area/playground.ts @@ -22,7 +22,8 @@ function prepareData(): ChartData { stacking: 'normal', dataLabels: { enabled: true, - numberFormat: { + format: { + type: 'number', precision: 2, }, }, diff --git a/src/__stories__/__data__/bar-x/playground.ts b/src/__stories__/__data__/bar-x/playground.ts index 387698d9a..211a39cbf 100644 --- a/src/__stories__/__data__/bar-x/playground.ts +++ b/src/__stories__/__data__/bar-x/playground.ts @@ -21,7 +21,7 @@ function prepareData(): ChartData { borderRadius: 8, dataLabels: { enabled: true, - numberFormat: {precision: 1}, + format: {type: 'number', precision: 1}, }, }, ], diff --git a/src/__stories__/__data__/pie/continuous-legend.ts b/src/__stories__/__data__/pie/continuous-legend.ts index 8a7e9ee19..2d8914d59 100644 --- a/src/__stories__/__data__/pie/continuous-legend.ts +++ b/src/__stories__/__data__/pie/continuous-legend.ts @@ -22,7 +22,7 @@ function prepareData(): ChartData { type: 'pie', data, dataLabels: { - numberFormat: {unit: 'auto'}, + format: {type: 'number', unit: 'auto'}, }, }, ], @@ -37,6 +37,12 @@ function prepareData(): ChartData { stops, }, }, + tooltip: { + valueFormat: { + type: 'number', + unit: 'k', + }, + }, }; } diff --git a/src/__stories__/__data__/radar/basic.ts b/src/__stories__/__data__/radar/basic.ts index 1097b708c..d9ceb5d4c 100644 --- a/src/__stories__/__data__/radar/basic.ts +++ b/src/__stories__/__data__/radar/basic.ts @@ -33,6 +33,9 @@ function prepareData(): ChartData { title: { text: 'Heroes of Might and Magic 3 Units (dragons)', }, + tooltip: { + valueFormat: {type: 'number'}, + }, }; } diff --git a/src/__stories__/__data__/treemap/playground.ts b/src/__stories__/__data__/treemap/playground.ts index 152c21cfc..0dc0a273d 100644 --- a/src/__stories__/__data__/treemap/playground.ts +++ b/src/__stories__/__data__/treemap/playground.ts @@ -8,7 +8,7 @@ const prepareData = (): ChartData => { name: 'Example', dataLabels: { enabled: true, - numberFormat: {precision: 1}, + format: {type: 'number', precision: 1}, }, layoutAlgorithm: 'binary', levels: [ diff --git a/src/__stories__/__data__/waterfall/playground.ts b/src/__stories__/__data__/waterfall/playground.ts index 87d2955a5..441eb7685 100644 --- a/src/__stories__/__data__/waterfall/playground.ts +++ b/src/__stories__/__data__/waterfall/playground.ts @@ -24,7 +24,7 @@ function prepareData(): ChartData { {total: true, x: 12}, ], name: 'Profit', - dataLabels: {enabled: true, numberFormat: {precision: 2}}, + dataLabels: {enabled: true, format: {type: 'number', precision: 2}}, }, ], }, diff --git a/src/components/Tooltip/ChartTooltipContent.tsx b/src/components/Tooltip/ChartTooltipContent.tsx index ade52ddd3..fac3a5579 100644 --- a/src/components/Tooltip/ChartTooltipContent.tsx +++ b/src/components/Tooltip/ChartTooltipContent.tsx @@ -11,10 +11,11 @@ export interface ChartTooltipContentProps { xAxis?: ChartXAxis; yAxis?: ChartYAxis; renderer?: ChartTooltip['renderer']; + valueFormat?: ChartTooltip['valueFormat']; } export const ChartTooltipContent = (props: ChartTooltipContentProps) => { - const {hovered, xAxis, yAxis, renderer} = props; + const {hovered, xAxis, yAxis, renderer, valueFormat} = props; if (!hovered) { return null; @@ -23,7 +24,7 @@ export const ChartTooltipContent = (props: ChartTooltipContentProps) => { const customTooltip = renderer?.({hovered, xAxis, yAxis}); return isNil(customTooltip) ? ( - + ) : ( customTooltip ); diff --git a/src/components/Tooltip/DefaultContent.tsx b/src/components/Tooltip/DefaultContent.tsx index ce26a5e88..d82422db5 100644 --- a/src/components/Tooltip/DefaultContent.tsx +++ b/src/components/Tooltip/DefaultContent.tsx @@ -14,9 +14,11 @@ import type { TooltipDataChunkRadar, TooltipDataChunkSankey, TreemapSeriesData, + ValueFormat, WaterfallSeriesData, } from '../../types'; import {block, getDataCategoryValue, getWaterfallPointSubtotal} from '../../utils'; +import {getFormattedValue} from '../../utils/chart/format'; const b = block('tooltip'); @@ -24,6 +26,7 @@ type Props = { hovered: TooltipDataChunk[]; xAxis?: ChartXAxis; yAxis?: ChartYAxis; + valueFormat?: ValueFormat; }; const DEFAULT_DATE_FORMAT = 'DD.MM.YY'; @@ -75,7 +78,7 @@ const getMeasureValue = (data: TooltipDataChunk[], xAxis?: ChartXAxis, yAxis?: C return getXRowData(data[0]?.data, xAxis); }; -export const DefaultContent = ({hovered, xAxis, yAxis}: Props) => { +export const DefaultContent = ({hovered, xAxis, yAxis, valueFormat}: Props) => { const measureValue = getMeasureValue(hovered, xAxis, yAxis); return ( @@ -91,9 +94,13 @@ export const DefaultContent = ({hovered, xAxis, yAxis}: Props) => { case 'line': case 'area': case 'bar-x': { + const formattedValue = getFormattedValue({ + value: getYRowData(data, yAxis), + format: valueFormat, + }); const value = ( - {series.name}: {getYRowData(data, yAxis)} + {series.name}: {formattedValue} ); return ( @@ -105,10 +112,18 @@ export const DefaultContent = ({hovered, xAxis, yAxis}: Props) => { } case 'waterfall': { const isTotal = get(data, 'total', false); - const subTotal = getWaterfallPointSubtotal( + const subTotalValue = getWaterfallPointSubtotal( data as WaterfallSeriesData, series as PreparedWaterfallSeries, ); + const subTotal = getFormattedValue({ + value: subTotalValue, + format: valueFormat, + }); + const formattedValue = getFormattedValue({ + value: getYRowData(data, yAxis), + format: valueFormat, + }); return (
@@ -119,7 +134,7 @@ export const DefaultContent = ({hovered, xAxis, yAxis}: Props) => {
{series.name}  - {getYRowData(data, yAxis)} + {formattedValue}
)} @@ -130,9 +145,13 @@ export const DefaultContent = ({hovered, xAxis, yAxis}: Props) => { ); } case 'bar-y': { + const formattedValue = getFormattedValue({ + value: getXRowData(data, xAxis), + format: valueFormat, + }); const value = ( - {series.name}: {getXRowData(data, xAxis)} + {series.name}: {formattedValue} ); return ( @@ -145,18 +164,26 @@ export const DefaultContent = ({hovered, xAxis, yAxis}: Props) => { case 'pie': case 'treemap': { const seriesData = data as PreparedPieSeries | TreemapSeriesData; + const formattedValue = getFormattedValue({ + value: seriesData.value, + format: valueFormat, + }); return (
{seriesData.name || seriesData.id}  - {seriesData.value} + {formattedValue}
); } case 'sankey': { const {target, data: source} = seriesItem as TooltipDataChunkSankey; const value = source.links.find((d) => d.name === target?.name)?.value; + const formattedValue = getFormattedValue({ + value, + format: valueFormat, + }); return (
@@ -165,7 +192,7 @@ export const DefaultContent = ({hovered, xAxis, yAxis}: Props) => { style={{backgroundColor: source.color}} />
- {source.name} {target?.name}: {value} + {source.name} {target?.name}: {formattedValue}
); @@ -173,11 +200,15 @@ export const DefaultContent = ({hovered, xAxis, yAxis}: Props) => { case 'radar': { const radarSeries = series as PreparedRadarSeries; const seriesData = data as RadarSeriesData; + const formattedValue = getFormattedValue({ + value: seriesData.value, + format: valueFormat, + }); const value = ( {radarSeries.name || radarSeries.id}  - {seriesData.value} + {formattedValue} ); diff --git a/src/components/Tooltip/index.tsx b/src/components/Tooltip/index.tsx index d82f5f189..83e062f1f 100644 --- a/src/components/Tooltip/index.tsx +++ b/src/components/Tooltip/index.tsx @@ -61,6 +61,7 @@ export const Tooltip = (props: TooltipProps) => { xAxis={xAxis} yAxis={yAxis as ChartYAxis} renderer={tooltip.renderer} + valueFormat={tooltip.valueFormat} />
diff --git a/src/hooks/useSeries/prepare-area.ts b/src/hooks/useSeries/prepare-area.ts index 331080368..30817c764 100644 --- a/src/hooks/useSeries/prepare-area.ts +++ b/src/hooks/useSeries/prepare-area.ts @@ -84,8 +84,7 @@ export function prepareArea(args: PrepareAreaSeriesArgs) { padding: get(series, 'dataLabels.padding', DEFAULT_DATALABELS_PADDING), allowOverlap: get(series, 'dataLabels.allowOverlap', false), html: get(series, 'dataLabels.html', false), - numberFormat: series.dataLabels?.numberFormat, - dateFormat: series.dataLabels?.dateFormat, + format: series.dataLabels?.format, }, marker: prepareMarker(series, seriesOptions), cursor: get(series, 'cursor', null), diff --git a/src/hooks/useSeries/prepare-bar-x.ts b/src/hooks/useSeries/prepare-bar-x.ts index 33c8b8729..816204937 100644 --- a/src/hooks/useSeries/prepare-bar-x.ts +++ b/src/hooks/useSeries/prepare-bar-x.ts @@ -45,8 +45,7 @@ export function prepareBarXSeries(args: PrepareBarXSeriesArgs): PreparedSeries[] allowOverlap: series.dataLabels?.allowOverlap || false, padding: get(series, 'dataLabels.padding', DEFAULT_DATALABELS_PADDING), html: get(series, 'dataLabels.html', false), - numberFormat: series.dataLabels?.numberFormat, - dateFormat: series.dataLabels?.dateFormat, + format: series.dataLabels?.format, }, cursor: get(series, 'cursor', null), yAxis: get(series, 'yAxis', 0), diff --git a/src/hooks/useSeries/prepare-bar-y.ts b/src/hooks/useSeries/prepare-bar-y.ts index dcc7256e0..02fef6db3 100644 --- a/src/hooks/useSeries/prepare-bar-y.ts +++ b/src/hooks/useSeries/prepare-bar-y.ts @@ -3,7 +3,7 @@ import get from 'lodash/get'; import type {BarYSeries, ChartSeriesOptions} from '../../types'; import {getLabelsSize, getUniqId} from '../../utils'; -import {getFormattedDataLabel} from '../useShapes/data-labels'; +import {getFormattedValue} from '../../utils/chart/format'; import {DEFAULT_DATALABELS_STYLE} from './constants'; import type {PreparedBarYSeries, PreparedLegend, PreparedSeries} from './types'; @@ -21,9 +21,7 @@ function prepareDataLabels(series: BarYSeries) { const style = Object.assign({}, DEFAULT_DATALABELS_STYLE, series.dataLabels?.style); const html = get(series, 'dataLabels.html', false); const labels = enabled - ? series.data.map((d) => - getFormattedDataLabel({value: d.x || d.label, ...series.dataLabels}), - ) + ? series.data.map((d) => getFormattedValue({value: d.x || d.label, ...series.dataLabels})) : []; const {maxHeight = 0, maxWidth = 0} = getLabelsSize({ labels, @@ -39,8 +37,7 @@ function prepareDataLabels(series: BarYSeries) { maxHeight, maxWidth, html, - numberFormat: series.dataLabels?.numberFormat, - dateFormat: series.dataLabels?.dateFormat, + format: series.dataLabels?.format, }; } diff --git a/src/hooks/useSeries/prepare-line.ts b/src/hooks/useSeries/prepare-line.ts index f78cd2adf..7126c5616 100644 --- a/src/hooks/useSeries/prepare-line.ts +++ b/src/hooks/useSeries/prepare-line.ts @@ -116,8 +116,7 @@ export function prepareLineSeries(args: PrepareLineSeriesArgs) { padding: get(series, 'dataLabels.padding', DEFAULT_DATALABELS_PADDING), allowOverlap: get(series, 'dataLabels.allowOverlap', false), html: get(series, 'dataLabels.html', false), - numberFormat: series.dataLabels?.numberFormat, - dateFormat: series.dataLabels?.dateFormat, + format: series.dataLabels?.format, }, marker: prepareMarker(series, seriesOptions), dashStyle: dashStyle as DashStyle, diff --git a/src/hooks/useSeries/prepare-pie.ts b/src/hooks/useSeries/prepare-pie.ts index acc3973d1..d80f103cb 100644 --- a/src/hooks/useSeries/prepare-pie.ts +++ b/src/hooks/useSeries/prepare-pie.ts @@ -36,8 +36,7 @@ export function preparePieSeries(args: PreparePieSeriesArgs) { distance: get(series, 'dataLabels.distance', 25), connectorCurve: get(series, 'dataLabels.connectorCurve', 'basic'), html: get(series, 'dataLabels.html', false), - numberFormat: series.dataLabels?.numberFormat, - dateFormat: series.dataLabels?.dateFormat, + format: series.dataLabels?.format, }, label: dataItem.label, value: dataItem.value, diff --git a/src/hooks/useSeries/prepare-radar.ts b/src/hooks/useSeries/prepare-radar.ts index dcbf37e98..391f45401 100644 --- a/src/hooks/useSeries/prepare-radar.ts +++ b/src/hooks/useSeries/prepare-radar.ts @@ -84,8 +84,7 @@ export function prepareRadarSeries(args: PrepareRadarSeriesArgs) { padding: get(series, 'dataLabels.padding', DEFAULT_DATALABELS_PADDING), allowOverlap: get(series, 'dataLabels.allowOverlap', false), html: get(series, 'dataLabels.html', false), - numberFormat: series.dataLabels?.numberFormat, - dateFormat: series.dataLabels?.dateFormat, + format: series.dataLabels?.format, }, cursor: get(series, 'cursor', null), marker: prepareMarker(series, seriesOptions), diff --git a/src/hooks/useSeries/prepare-sankey.ts b/src/hooks/useSeries/prepare-sankey.ts index 5969c83a5..9acadc39e 100644 --- a/src/hooks/useSeries/prepare-sankey.ts +++ b/src/hooks/useSeries/prepare-sankey.ts @@ -33,8 +33,7 @@ export function prepareSankeySeries(args: PrepareSankeySeriesArgs) { dataLabels: { enabled: get(s, 'dataLabels.enabled', true), style: Object.assign({}, DEFAULT_DATALABELS_STYLE, s.dataLabels?.style), - numberFormat: s.dataLabels?.numberFormat, - dateFormat: s.dataLabels?.dateFormat, + format: s.dataLabels?.format, }, id, type: s.type, diff --git a/src/hooks/useSeries/prepare-treemap.ts b/src/hooks/useSeries/prepare-treemap.ts index 704e105c2..9272a6abd 100644 --- a/src/hooks/useSeries/prepare-treemap.ts +++ b/src/hooks/useSeries/prepare-treemap.ts @@ -34,8 +34,7 @@ export function prepareTreemap(args: PrepareTreemapSeriesArgs) { allowOverlap: get(s, 'dataLabels.allowOverlap', false), html: get(s, 'dataLabels.html', false), align: get(s, 'dataLabels.align', 'left'), - numberFormat: s.dataLabels?.numberFormat, - dateFormat: s.dataLabels?.dateFormat, + format: s.dataLabels?.format, }, id, type: s.type, diff --git a/src/hooks/useSeries/prepare-waterfall.ts b/src/hooks/useSeries/prepare-waterfall.ts index 798415790..f18289d4a 100644 --- a/src/hooks/useSeries/prepare-waterfall.ts +++ b/src/hooks/useSeries/prepare-waterfall.ts @@ -42,8 +42,7 @@ export function prepareWaterfallSeries(args: PrepareWaterfallSeriesArgs): Prepar allowOverlap: series.dataLabels?.allowOverlap || false, padding: get(series, 'dataLabels.padding', DEFAULT_DATALABELS_PADDING), html: get(series, 'dataLabels.html', false), - numberFormat: series.dataLabels?.numberFormat, - dateFormat: series.dataLabels?.dateFormat, + format: series.dataLabels?.format, }, cursor: get(series, 'cursor', null), }; diff --git a/src/hooks/useSeries/types.ts b/src/hooks/useSeries/types.ts index 0ec65cdf7..e15038839 100644 --- a/src/hooks/useSeries/types.ts +++ b/src/hooks/useSeries/types.ts @@ -32,10 +32,10 @@ import type { SymbolLegendSymbolOptions, TreemapSeries, TreemapSeriesData, + ValueFormat, WaterfallSeries, WaterfallSeriesData, } from '../../types'; -import type {FormatNumberOptions} from '../../types/formatter'; export type RectLegendSymbol = { shape: 'rect'; @@ -151,8 +151,7 @@ export type PreparedBarXSeries = { allowOverlap: boolean; padding: number; html: boolean; - dateFormat?: string; - numberFormat?: FormatNumberOptions; + format?: ValueFormat; }; borderRadius: number; yAxis: number; @@ -170,8 +169,7 @@ export type PreparedBarYSeries = { maxHeight: number; maxWidth: number; html: boolean; - dateFormat?: string; - numberFormat?: FormatNumberOptions; + format?: ValueFormat; }; borderRadius: number; } & BasePreparedSeries; @@ -198,8 +196,7 @@ export type PreparedPieSeries = { distance: number; connectorCurve: ConnectorCurve; html: boolean; - dateFormat?: string; - numberFormat?: FormatNumberOptions; + format?: ValueFormat; }; states: { hover: { @@ -220,8 +217,7 @@ export type PreparedLineSeries = { padding: number; allowOverlap: boolean; html: boolean; - dateFormat?: string; - numberFormat?: FormatNumberOptions; + format?: ValueFormat; }; marker: { states: { @@ -260,8 +256,7 @@ export type PreparedAreaSeries = { padding: number; allowOverlap: boolean; html: boolean; - dateFormat?: string; - numberFormat?: FormatNumberOptions; + format?: ValueFormat; }; marker: { states: { @@ -294,8 +289,7 @@ export type PreparedTreemapSeries = { allowOverlap: boolean; html: boolean; align: Required['dataLabels']>['align']; - dateFormat?: string; - numberFormat?: FormatNumberOptions; + format?: ValueFormat; }; layoutAlgorithm: `${LayoutAlgorithm}`; } & BasePreparedSeries & @@ -310,8 +304,7 @@ export type PreparedWaterfallSeries = { allowOverlap: boolean; padding: number; html: boolean; - dateFormat?: string; - numberFormat?: FormatNumberOptions; + format?: ValueFormat; }; positiveColor: string; negativeColor: string; @@ -323,8 +316,7 @@ export type PreparedSankeySeries = { dataLabels: { enabled: boolean; style: BaseTextStyle; - dateFormat?: string; - numberFormat?: FormatNumberOptions; + format?: ValueFormat; }; } & BasePreparedSeries & Omit; @@ -342,8 +334,7 @@ export type PreparedRadarSeries = { padding: number; allowOverlap: boolean; html: boolean; - dateFormat?: string; - numberFormat?: FormatNumberOptions; + format?: ValueFormat; }; marker: { states: { diff --git a/src/hooks/useShapes/area/prepare-data.ts b/src/hooks/useShapes/area/prepare-data.ts index 5f9b1999e..2613174f1 100644 --- a/src/hooks/useShapes/area/prepare-data.ts +++ b/src/hooks/useShapes/area/prepare-data.ts @@ -2,16 +2,16 @@ import {group, sort} from 'd3'; import type {AreaSeriesData, HtmlItem, LabelData} from '../../../types'; import {getDataCategoryValue, getLabelsSize, getLeftPosition} from '../../../utils'; +import {getFormattedValue} from '../../../utils/chart/format'; import type {ChartScale} from '../../useAxisScales'; import type {PreparedAxis} from '../../useChartOptions/types'; import type {PreparedAreaSeries} from '../../useSeries/types'; -import {getFormattedDataLabel} from '../data-labels'; import {getXValue, getYValue} from '../utils'; import type {MarkerData, PointData, PreparedAreaData} from './types'; function getLabelData(point: PointData, series: PreparedAreaSeries, xMax: number) { - const text = getFormattedDataLabel({ + const text = getFormattedValue({ value: point.data.label || point.data.y, ...series.dataLabels, }); diff --git a/src/hooks/useShapes/bar-x/prepare-data.ts b/src/hooks/useShapes/bar-x/prepare-data.ts index 6833f60b3..ba4282698 100644 --- a/src/hooks/useShapes/bar-x/prepare-data.ts +++ b/src/hooks/useShapes/bar-x/prepare-data.ts @@ -4,11 +4,11 @@ import get from 'lodash/get'; import type {BarXSeriesData, LabelData} from '../../../types'; import {getDataCategoryValue, getLabelsSize} from '../../../utils'; +import {getFormattedValue} from '../../../utils/chart/format'; import type {ChartScale} from '../../useAxisScales'; import type {PreparedAxis} from '../../useChartOptions/types'; import type {PreparedBarXSeries, PreparedSeriesOptions} from '../../useSeries/types'; import {MIN_BAR_GAP, MIN_BAR_GROUP_GAP, MIN_BAR_WIDTH} from '../constants'; -import {getFormattedDataLabel} from '../data-labels'; import type {PreparedBarXData} from './types'; @@ -17,7 +17,7 @@ function getLabelData(d: PreparedBarXData): LabelData | undefined { return undefined; } - const text = getFormattedDataLabel({ + const text = getFormattedValue({ value: d.data.label || d.data.y, ...d.series.dataLabels, }); diff --git a/src/hooks/useShapes/bar-y/prepare-data.ts b/src/hooks/useShapes/bar-y/prepare-data.ts index 9db1a51df..4ca50a565 100644 --- a/src/hooks/useShapes/bar-y/prepare-data.ts +++ b/src/hooks/useShapes/bar-y/prepare-data.ts @@ -4,11 +4,11 @@ import get from 'lodash/get'; import type {BarYSeriesData, LabelData} from '../../../types'; import {getDataCategoryValue, getLabelsSize} from '../../../utils'; +import {getFormattedValue} from '../../../utils/chart/format'; import type {ChartScale} from '../../useAxisScales'; import type {PreparedAxis} from '../../useChartOptions/types'; import type {PreparedBarYSeries, PreparedSeriesOptions} from '../../useSeries/types'; import {MIN_BAR_GAP, MIN_BAR_GROUP_GAP, MIN_BAR_WIDTH} from '../constants'; -import {getFormattedDataLabel} from '../data-labels'; import type {PreparedBarYData} from './types'; @@ -78,7 +78,7 @@ function setLabel(prepared: PreparedBarYData) { } const data = prepared.data; - const content = getFormattedDataLabel({value: data.label || data.x, ...dataLabels}); + const content = getFormattedValue({value: data.label || data.x, ...dataLabels}); const {maxHeight: height, maxWidth: width} = getLabelsSize({ labels: [content], style: dataLabels.style, diff --git a/src/hooks/useShapes/data-labels.ts b/src/hooks/useShapes/data-labels.ts deleted file mode 100644 index df1fc84e5..000000000 --- a/src/hooks/useShapes/data-labels.ts +++ /dev/null @@ -1,27 +0,0 @@ -import {dateTime} from '@gravity-ui/date-utils'; - -import {formatNumber} from '../../libs'; -import type {FormatNumberOptions} from '../../types/formatter'; - -export function getFormattedDataLabel(args: { - value: string | number | undefined | null; - dateFormat?: string; - numberFormat?: FormatNumberOptions; -}) { - const {value, numberFormat, dateFormat} = args; - - const valueAsNumber = Number(value); - - if (dateFormat) { - const date = dateTime({input: value}); - if (date?.isValid()) { - return date.format(dateFormat); - } - } - - if (!Number.isNaN(valueAsNumber) && numberFormat) { - return formatNumber(valueAsNumber, numberFormat); - } - - return String(value); -} diff --git a/src/hooks/useShapes/line/prepare-data.ts b/src/hooks/useShapes/line/prepare-data.ts index d9ae6a6d1..010dbea09 100644 --- a/src/hooks/useShapes/line/prepare-data.ts +++ b/src/hooks/useShapes/line/prepare-data.ts @@ -1,16 +1,16 @@ import type {HtmlItem, LabelData} from '../../../types'; import {getLabelsSize, getLeftPosition} from '../../../utils'; +import {getFormattedValue} from '../../../utils/chart/format'; import type {ChartScale} from '../../useAxisScales'; import type {PreparedAxis} from '../../useChartOptions/types'; import type {PreparedLineSeries} from '../../useSeries/types'; import type {PreparedSplit} from '../../useSplit/types'; -import {getFormattedDataLabel} from '../data-labels'; import {getXValue, getYValue} from '../utils'; import type {MarkerData, PointData, PreparedLineData} from './types'; function getLabelData(point: PointData, series: PreparedLineSeries, xMax: number) { - const text = getFormattedDataLabel({ + const text = getFormattedValue({ value: point.data.label || point.data.y, ...series.dataLabels, }); diff --git a/src/hooks/useShapes/pie/prepare-data.ts b/src/hooks/useShapes/pie/prepare-data.ts index 8dd834265..dfd0a3516 100644 --- a/src/hooks/useShapes/pie/prepare-data.ts +++ b/src/hooks/useShapes/pie/prepare-data.ts @@ -8,8 +8,8 @@ import { getLeftPosition, isLabelsOverlapping, } from '../../../utils'; +import {getFormattedValue} from '../../../utils/chart/format'; import type {PreparedPieSeries} from '../../useSeries/types'; -import {getFormattedDataLabel} from '../data-labels'; import type {PieConnectorData, PieLabelData, PreparedPieData, SegmentData} from './types'; import {getCurveFactory, pieGenerator} from './utils'; @@ -149,7 +149,7 @@ export function preparePieData(args: Args): PreparedPieData[] { series.forEach((d, index) => { const prevLabel = labels[labels.length - 1]; - const text = getFormattedDataLabel({ + const text = getFormattedValue({ value: d.data.label || d.data.value, ...d.dataLabels, }); diff --git a/src/hooks/useShapes/radar/prepare-data.ts b/src/hooks/useShapes/radar/prepare-data.ts index 022117efd..28c67928e 100644 --- a/src/hooks/useShapes/radar/prepare-data.ts +++ b/src/hooks/useShapes/radar/prepare-data.ts @@ -2,8 +2,8 @@ import {curveLinearClosed, line, range, scaleLinear} from 'd3'; import type {HtmlItem} from '../../../types'; import {getLabelsSize} from '../../../utils'; +import {getFormattedValue} from '../../../utils/chart/format'; import type {PreparedRadarSeries} from '../../useSeries/types'; -import {getFormattedDataLabel} from '../data-labels'; import type {PreparedRadarData, RadarGridData, RadarMarkerData} from './types'; @@ -137,7 +137,7 @@ export function prepareRadarData(args: Args): PreparedRadarData[] { const {style} = dataLabels; const shouldUseHtml = dataLabels.html; data.labels = categories.map((category, index) => { - const text = getFormattedDataLabel({ + const text = getFormattedValue({ value: category.key, ...dataLabels, }); diff --git a/src/hooks/useShapes/sankey/prepare-data.ts b/src/hooks/useShapes/sankey/prepare-data.ts index 30131c9e9..244bcae87 100644 --- a/src/hooks/useShapes/sankey/prepare-data.ts +++ b/src/hooks/useShapes/sankey/prepare-data.ts @@ -1,8 +1,8 @@ import {sankey, sankeyLinkHorizontal} from 'd3-sankey'; import type {HtmlItem, SankeySeriesData} from '../../../types'; +import {getFormattedValue} from '../../../utils/chart/format'; import type {PreparedSankeySeries} from '../../useSeries/types'; -import {getFormattedDataLabel} from '../data-labels'; import type {PreparedSankeyData, SankeyDataLabel} from './types'; @@ -77,7 +77,7 @@ export function prepareSankeyData(args: { const x1 = d.x1 ?? 0; const y0 = d.y0 ?? 0; const y1 = d.y1 ?? 0; - const text = getFormattedDataLabel({value: d.name, ...dataLabels}); + const text = getFormattedValue({value: d.name, ...dataLabels}); return { text, diff --git a/src/hooks/useShapes/treemap/prepare-data.ts b/src/hooks/useShapes/treemap/prepare-data.ts index 20145157c..c950207d6 100644 --- a/src/hooks/useShapes/treemap/prepare-data.ts +++ b/src/hooks/useShapes/treemap/prepare-data.ts @@ -12,8 +12,8 @@ import type {HierarchyRectangularNode} from 'd3'; import {LayoutAlgorithm} from '../../../constants'; import type {HtmlItem, TreemapSeriesData} from '../../../types'; import {getLabelsSize} from '../../../utils'; +import {getFormattedValue} from '../../../utils/chart/format'; import type {PreparedTreemapSeries} from '../../useSeries/types'; -import {getFormattedDataLabel} from '../data-labels'; import type {PreparedTreemapData, TreemapLabelData} from './types'; @@ -34,7 +34,7 @@ function getLabels(args: { const texts = Array.isArray(d.data.name) ? d.data.name : [d.data.name]; texts.forEach((text, index) => { - const label = getFormattedDataLabel({value: text, ...args.options}); + const label = getFormattedValue({value: text, ...args.options}); const {maxHeight: lineHeight, maxWidth: labelWidth} = getLabelsSize({labels: [label], html}) ?? {}; const left = d.x0 + padding; diff --git a/src/hooks/useShapes/waterfall/prepare-data.ts b/src/hooks/useShapes/waterfall/prepare-data.ts index 967fa7888..b845228b4 100644 --- a/src/hooks/useShapes/waterfall/prepare-data.ts +++ b/src/hooks/useShapes/waterfall/prepare-data.ts @@ -4,11 +4,11 @@ import sortBy from 'lodash/sortBy'; import type {LabelData, WaterfallSeriesData} from '../../../types'; import {getLabelsSize} from '../../../utils'; +import {getFormattedValue} from '../../../utils/chart/format'; import type {ChartScale} from '../../useAxisScales'; import type {PreparedAxis} from '../../useChartOptions/types'; import type {PreparedSeriesOptions, PreparedWaterfallSeries} from '../../useSeries/types'; import {MIN_BAR_GAP, MIN_BAR_WIDTH} from '../constants'; -import {getFormattedDataLabel} from '../data-labels'; import {getXValue, getYValue} from '../utils'; import type {PreparedWaterfallData} from './types'; @@ -18,7 +18,7 @@ function getLabelData(d: PreparedWaterfallData, plotHeight: number): LabelData | return undefined; } - const text = getFormattedDataLabel({value: d.data.label || d.subTotal, ...d.series.dataLabels}); + const text = getFormattedValue({value: d.data.label || d.subTotal, ...d.series.dataLabels}); const style = d.series.dataLabels.style; const {maxHeight: height, maxWidth: width} = getLabelsSize({labels: [text], style}); diff --git a/src/types/chart/base.ts b/src/types/chart/base.ts index 1233a1988..2cc5b8237 100644 --- a/src/types/chart/base.ts +++ b/src/types/chart/base.ts @@ -1,6 +1,15 @@ import type {FormatNumberOptions} from '../formatter'; import type {MeaningfulAny} from '../misc'; +type NumberFormat = { + type: 'number'; +} & FormatNumberOptions; +type DateFormat = { + type: 'date'; + format?: string; +}; +export type ValueFormat = NumberFormat | DateFormat; + export interface BaseSeries { /** Initial visibility of the series */ visible?: boolean; @@ -29,10 +38,8 @@ export interface BaseSeries { * @default false * */ html?: boolean; - /** If specified, label formatting is applied with the selected date format. */ - dateFormat?: string; - /** Formatting settings for numeric values */ - numberFormat?: FormatNumberOptions; + /** Formatting settings for labels. */ + format?: ValueFormat; }; /** You can set the cursor to "pointer" if you have click events attached to the series, to signal to the user that the points and lines can be clicked. */ cursor?: string; diff --git a/src/types/chart/tooltip.ts b/src/types/chart/tooltip.ts index 8587b0c35..71e92f2d7 100644 --- a/src/types/chart/tooltip.ts +++ b/src/types/chart/tooltip.ts @@ -4,6 +4,7 @@ import type {AreaSeries, AreaSeriesData} from './area'; import type {ChartXAxis, ChartYAxis} from './axis'; import type {BarXSeries, BarXSeriesData} from './bar-x'; import type {BarYSeries, BarYSeriesData} from './bar-y'; +import type {ValueFormat} from './base'; import type {LineSeries, LineSeriesData} from './line'; import type {PieSeries, PieSeriesData} from './pie'; import type {RadarSeries, RadarSeriesCategory, RadarSeriesData} from './radar'; @@ -110,4 +111,6 @@ export interface ChartTooltip { }; /** Show tooltip at most once per every ```throttle``` milliseconds */ throttle?: number; + /** Formatting settings for tooltip value. */ + valueFormat?: ValueFormat; } diff --git a/src/utils/chart/format.ts b/src/utils/chart/format.ts new file mode 100644 index 000000000..e0469a9de --- /dev/null +++ b/src/utils/chart/format.ts @@ -0,0 +1,25 @@ +import {dateTime} from '@gravity-ui/date-utils'; + +import {formatNumber} from '../../libs'; +import type {ValueFormat} from '../../types'; + +export function getFormattedValue(args: { + value: string | number | undefined | null; + format?: ValueFormat; +}) { + const {value, format} = args; + + switch (format?.type) { + case 'number': { + return formatNumber(Number(value), format); + } + case 'date': { + const date = dateTime({input: value}); + if (date?.isValid()) { + return date.format(format.format); + } + } + } + + return String(value); +}