@visactor/vseed 0.4.26 → 0.4.27
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/index.cjs +1 -1
- package/dist/cjs/index.cjs.map +1 -1
- package/dist/esm/pipeline/spec/chart/pipes/annotation/annotationDifferenceLine.js +65 -2
- package/dist/esm/pipeline/spec/chart/pipes/annotation/annotationDifferenceLine.js.map +1 -1
- package/dist/esm/pipeline/spec/chart/pipes/annotation/annotationDifferenceLineCommon.d.ts +2 -1
- package/dist/esm/pipeline/spec/chart/pipes/annotation/annotationDifferenceLineCommon.js +14 -7
- package/dist/esm/pipeline/spec/chart/pipes/annotation/annotationDifferenceLineCommon.js.map +1 -1
- package/dist/umd/index.js +75 -8
- package/dist/umd/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { autoFormatter, createFormatter, findAllMeasures } from "../../../../utils/index.js";
|
|
2
|
+
import { isDimensionSelector, isFieldSelector, isMeasureSelector, isPartialDatumSelector } from "../../../../../dataSelector/index.js";
|
|
3
|
+
import { isEmpty } from "remeda";
|
|
1
4
|
import { ANNOTATION_Z_INDEX } from "../../../../utils/constant.js";
|
|
2
5
|
import { buildDifferenceCoordinateDatum, buildDifferenceText, getDifferenceLineStackResolveMode, getRuntimeDifferenceValue, inferDifferenceBracketDirection, inferDifferenceConnectDirection, resolveDifferenceAnchor, usesDifferenceLineElementStackEnd } from "./annotationDifferenceLineCommon.js";
|
|
3
6
|
const DEFAULT_LINE_COLOR = '#BCC1CB';
|
|
@@ -15,7 +18,58 @@ const DEFAULT_BRACKET_LINE_DASH = [
|
|
|
15
18
|
2,
|
|
16
19
|
2
|
|
17
20
|
];
|
|
21
|
+
const DEFAULT_PERCENT_DIFFERENCE_FORMAT = {
|
|
22
|
+
type: 'percent',
|
|
23
|
+
fractionDigits: 2
|
|
24
|
+
};
|
|
18
25
|
const getDifferenceLinePath = (index, total)=>1 === total ? 'annotationDifferenceLine' : `annotationDifferenceLine[${index}]`;
|
|
26
|
+
const toArray = (value)=>{
|
|
27
|
+
if (Array.isArray(value)) return value;
|
|
28
|
+
return null == value ? [] : [
|
|
29
|
+
value
|
|
30
|
+
];
|
|
31
|
+
};
|
|
32
|
+
const getAxisFormatter = (spec)=>{
|
|
33
|
+
const valueAxisOrient = 'horizontal' === spec.direction ? 'bottom' : 'left';
|
|
34
|
+
const formatMethod = spec.axes?.find((axis)=>axis.orient === valueAxisOrient)?.label?.formatMethod;
|
|
35
|
+
return 'function' == typeof formatMethod ? (value)=>String(formatMethod(value) ?? value) : void 0;
|
|
36
|
+
};
|
|
37
|
+
const getExplicitMeasureFormat = (measure)=>{
|
|
38
|
+
if (measure?.numFormat && !isEmpty(measure.numFormat)) return measure.numFormat;
|
|
39
|
+
if (measure?.format && !isEmpty(measure.format)) return measure.format;
|
|
40
|
+
};
|
|
41
|
+
const inferMeasureIdFromDatum = (anchor, measureIds)=>{
|
|
42
|
+
const candidateValues = [
|
|
43
|
+
anchor.matchedDatum,
|
|
44
|
+
anchor.coordinateDatum
|
|
45
|
+
];
|
|
46
|
+
for (const datum of candidateValues){
|
|
47
|
+
if (!datum) continue;
|
|
48
|
+
const candidates = measureIds.filter((measureId)=>Number(datum[measureId]) === anchor.value);
|
|
49
|
+
if (1 === candidates.length) return candidates[0];
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
const inferMeasureIdFromSelector = (selectorValue, measureIdSet)=>{
|
|
53
|
+
const matchedMeasureIds = new Set();
|
|
54
|
+
for (const currentSelector of toArray(selectorValue)){
|
|
55
|
+
if (isMeasureSelector(currentSelector)) {
|
|
56
|
+
if (measureIdSet.has(currentSelector.field)) matchedMeasureIds.add(currentSelector.field);
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
if (!(isFieldSelector(currentSelector) || isDimensionSelector(currentSelector))) {
|
|
60
|
+
if (isPartialDatumSelector(currentSelector)) Object.keys(currentSelector).forEach((field)=>{
|
|
61
|
+
if (measureIdSet.has(field)) matchedMeasureIds.add(field);
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return 1 === matchedMeasureIds.size ? Array.from(matchedMeasureIds)[0] : void 0;
|
|
66
|
+
};
|
|
67
|
+
const resolveDifferenceMeasureId = (anchor, selectorValue, measureIds)=>{
|
|
68
|
+
if ('element' !== anchor.mode) return;
|
|
69
|
+
if (1 === measureIds.length) return measureIds[0];
|
|
70
|
+
const measureIdSet = new Set(measureIds);
|
|
71
|
+
return inferMeasureIdFromSelector(selectorValue, measureIdSet) ?? inferMeasureIdFromDatum(anchor, measureIds);
|
|
72
|
+
};
|
|
19
73
|
const assertDifferenceLineConfig = (value, path)=>{
|
|
20
74
|
if ('object' != typeof value || null === value || Array.isArray(value)) throw new Error(`${path} must be an object`);
|
|
21
75
|
const start = value.start;
|
|
@@ -38,6 +92,10 @@ const annotationDifferenceLine_annotationDifferenceLine = (spec, context)=>{
|
|
|
38
92
|
const stackResolveMode = getDifferenceLineStackResolveMode(vseed, advancedVSeed);
|
|
39
93
|
const useElementStackEnd = usesDifferenceLineElementStackEnd(vseed, advancedVSeed);
|
|
40
94
|
const isBracketChart = 'line' === vseed.chartType || 'area' === vseed.chartType;
|
|
95
|
+
const measures = findAllMeasures(advancedVSeed.measures);
|
|
96
|
+
const measureIds = measures.map((measure)=>measure.id);
|
|
97
|
+
const axisFormatter = getAxisFormatter(chartSpec);
|
|
98
|
+
const percentFormatter = createFormatter(DEFAULT_PERCENT_DIFFERENCE_FORMAT);
|
|
41
99
|
const markLine = annotationDifferenceLineList.flatMap((annotationDifferenceLine, index)=>{
|
|
42
100
|
try {
|
|
43
101
|
assertDifferenceLineConfig(annotationDifferenceLine, getDifferenceLinePath(index, annotationDifferenceLineList.length));
|
|
@@ -71,6 +129,11 @@ const annotationDifferenceLine_annotationDifferenceLine = (spec, context)=>{
|
|
|
71
129
|
const textBackgroundColor = annotationDifferenceLine.textBackgroundColor ?? theme?.textBackgroundColor ?? DEFAULT_TEXT_BACKGROUND_COLOR;
|
|
72
130
|
const textFontSize = annotationDifferenceLine.textFontSize ?? theme?.textFontSize ?? DEFAULT_TEXT_FONT_SIZE;
|
|
73
131
|
const differenceType = annotationDifferenceLine.differenceType ?? 'absolute';
|
|
132
|
+
const startMeasureId = resolveDifferenceMeasureId(start, annotationDifferenceLine.start.selector, measureIds);
|
|
133
|
+
const endMeasureId = resolveDifferenceMeasureId(end, annotationDifferenceLine.end.selector, measureIds);
|
|
134
|
+
const sameMeasure = void 0 !== startMeasureId && startMeasureId === endMeasureId;
|
|
135
|
+
const explicitMeasureFormat = sameMeasure ? getExplicitMeasureFormat(measures.find((measure)=>measure.id === startMeasureId)) : void 0;
|
|
136
|
+
const differenceFormatter = 'percent' === differenceType ? percentFormatter : explicitMeasureFormat ? createFormatter(explicitMeasureFormat) : axisFormatter ?? autoFormatter;
|
|
74
137
|
const label = usesRuntimeStackEnd ? {
|
|
75
138
|
visible: true,
|
|
76
139
|
position: 'middle',
|
|
@@ -85,7 +148,7 @@ const annotationDifferenceLine_annotationDifferenceLine = (spec, context)=>{
|
|
|
85
148
|
anchor: end,
|
|
86
149
|
seriesData,
|
|
87
150
|
useElementStackEnd: usesRuntimeStackEnd
|
|
88
|
-
}), differenceType);
|
|
151
|
+
}), differenceType, differenceFormatter);
|
|
89
152
|
} catch {
|
|
90
153
|
return '';
|
|
91
154
|
}
|
|
@@ -109,7 +172,7 @@ const annotationDifferenceLine_annotationDifferenceLine = (spec, context)=>{
|
|
|
109
172
|
visible: true,
|
|
110
173
|
position: 'middle',
|
|
111
174
|
refY: 0,
|
|
112
|
-
text: buildDifferenceText(start.value, end.value, differenceType),
|
|
175
|
+
text: buildDifferenceText(start.value, end.value, differenceType, differenceFormatter),
|
|
113
176
|
style: {
|
|
114
177
|
fill: textColor,
|
|
115
178
|
fontSize: textFontSize
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pipeline/spec/chart/pipes/annotation/annotationDifferenceLine.js","sources":["../../../../../../../src/pipeline/spec/chart/pipes/annotation/annotationDifferenceLine.ts"],"sourcesContent":["import type { IAreaChartSpec, IBarChartSpec, ICartesianSeries, ILineChartSpec, IMarkLineSpec } from '@visactor/vchart'\nimport type { AnnotationDifferenceLine, VChartSpecPipe } from 'src/types'\nimport { ANNOTATION_Z_INDEX } from '../../../../utils/constant'\nimport {\n buildDifferenceCoordinateDatum,\n buildDifferenceText,\n getDifferenceLineStackResolveMode,\n getRuntimeDifferenceValue,\n inferDifferenceBracketDirection,\n inferDifferenceConnectDirection,\n usesDifferenceLineElementStackEnd,\n resolveDifferenceAnchor,\n} from './annotationDifferenceLineCommon'\n\nconst DEFAULT_LINE_COLOR = '#BCC1CB'\nconst DEFAULT_TEXT_COLOR = '#ffffff'\nconst DEFAULT_TEXT_BACKGROUND_COLOR = '#BCC1CB'\nconst DEFAULT_TEXT_FONT_SIZE = 12\nconst DEFAULT_EXPAND_DISTANCE = 24\nconst DEFAULT_LINE_WIDTH = 2\nconst DEFAULT_CORNER_RADIUS = 4\nconst DEFAULT_LABEL_PADDING = 4\nconst DEFAULT_END_SYMBOL_SIZE = 12\nconst DEFAULT_END_SYMBOL_REF_X = -4\nconst DEFAULT_BRACKET_EXPAND_DISTANCE = 80\nconst DEFAULT_BRACKET_LINE_DASH: [number, number] = [2, 2]\n\nconst getDifferenceLinePath = (index: number, total: number) =>\n total === 1 ? 'annotationDifferenceLine' : `annotationDifferenceLine[${index}]`\n\nconst assertDifferenceLineConfig: (value: unknown, path: string) => asserts value is AnnotationDifferenceLine = (\n value,\n path,\n) => {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw new Error(`${path} must be an object`)\n }\n\n const start = (value as Record<string, unknown>).start\n if (typeof start !== 'object' || start === null || Array.isArray(start)) {\n throw new Error(`${path}.start is required`)\n }\n if ((start as Record<string, unknown>).selector == null) {\n throw new Error(`${path}.start.selector is required`)\n }\n\n const end = (value as Record<string, unknown>).end\n if (typeof end !== 'object' || end === null || Array.isArray(end)) {\n throw new Error(`${path}.end is required`)\n }\n if ((end as Record<string, unknown>).selector == null) {\n throw new Error(`${path}.end.selector is required`)\n }\n}\n\nexport const annotationDifferenceLine: VChartSpecPipe = (spec, context) => {\n const { advancedVSeed, vseed } = context\n const annotationDifferenceLine = advancedVSeed.annotation?.annotationDifferenceLine\n\n if (!annotationDifferenceLine) {\n return spec\n }\n\n const theme = advancedVSeed.config?.[vseed.chartType as 'column']?.annotation?.annotationDifferenceLine\n const annotationDifferenceLineList = Array.isArray(annotationDifferenceLine)\n ? annotationDifferenceLine\n : [annotationDifferenceLine]\n const dataset = advancedVSeed.dataset.flat()\n const chartSpec = spec as IBarChartSpec | ILineChartSpec | IAreaChartSpec\n const stackResolveMode = getDifferenceLineStackResolveMode(vseed, advancedVSeed)\n const useElementStackEnd = usesDifferenceLineElementStackEnd(vseed, advancedVSeed)\n const isBracketChart = vseed.chartType === 'line' || vseed.chartType === 'area'\n\n const markLine = annotationDifferenceLineList.flatMap((annotationDifferenceLine, index) => {\n try {\n assertDifferenceLineConfig(\n annotationDifferenceLine,\n getDifferenceLinePath(index, annotationDifferenceLineList.length),\n )\n\n const start = resolveDifferenceAnchor({\n dataset,\n selectorLabel: 'start',\n selectorValue: annotationDifferenceLine.start.selector,\n spec: chartSpec,\n stackResolveMode,\n allowSelectorFallback: !useElementStackEnd,\n })\n const end = resolveDifferenceAnchor({\n dataset,\n selectorLabel: 'end',\n selectorValue: annotationDifferenceLine.end.selector,\n spec: chartSpec,\n stackResolveMode,\n allowSelectorFallback: !useElementStackEnd,\n })\n\n if (!start || !end) {\n return []\n }\n\n if (start.mode !== end.mode) {\n return []\n }\n\n const usesRuntimeStackEnd =\n useElementStackEnd ||\n ((vseed.chartType === 'column' || vseed.chartType === 'bar') &&\n start.mode === 'element' &&\n stackResolveMode === 'auto')\n const useBracketStyle =\n isBracketChart ||\n ((vseed.chartType === 'column' || vseed.chartType === 'bar') &&\n start.mode === 'element' &&\n stackResolveMode === 'auto')\n const isStackedBarElementBracket =\n vseed.chartType === 'bar' && start.mode === 'element' && stackResolveMode === 'auto'\n const connectDirection = useBracketStyle\n ? isStackedBarElementBracket\n ? 'top'\n : inferDifferenceBracketDirection(start, end)\n : inferDifferenceConnectDirection(vseed, [start.value, end.value])\n\n const lineColor = annotationDifferenceLine.lineColor ?? theme?.lineColor ?? DEFAULT_LINE_COLOR\n const textColor = annotationDifferenceLine.textColor ?? theme?.textColor ?? DEFAULT_TEXT_COLOR\n const textBackgroundColor =\n annotationDifferenceLine.textBackgroundColor ?? theme?.textBackgroundColor ?? DEFAULT_TEXT_BACKGROUND_COLOR\n const textFontSize = annotationDifferenceLine.textFontSize ?? theme?.textFontSize ?? DEFAULT_TEXT_FONT_SIZE\n const differenceType = annotationDifferenceLine.differenceType ?? 'absolute'\n\n const label = usesRuntimeStackEnd\n ? {\n visible: true,\n position: 'middle',\n refY: 0,\n formatMethod: (_markData: any[], seriesData: any[]) => {\n try {\n return buildDifferenceText(\n getRuntimeDifferenceValue({\n anchor: start,\n seriesData,\n useElementStackEnd: usesRuntimeStackEnd,\n }),\n getRuntimeDifferenceValue({\n anchor: end,\n seriesData,\n useElementStackEnd: usesRuntimeStackEnd,\n }),\n differenceType,\n )\n } catch {\n return ''\n }\n },\n style: {\n fill: textColor,\n fontSize: textFontSize,\n },\n labelBackground: {\n visible: true,\n padding: DEFAULT_LABEL_PADDING,\n style: {\n fill: textBackgroundColor,\n fillOpacity: 1,\n stroke: lineColor,\n lineWidth: 1,\n cornerRadius: DEFAULT_CORNER_RADIUS,\n },\n },\n }\n : {\n visible: true,\n position: 'middle',\n refY: 0,\n text: buildDifferenceText(start.value, end.value, differenceType),\n style: {\n fill: textColor,\n fontSize: textFontSize,\n },\n labelBackground: {\n visible: true,\n padding: DEFAULT_LABEL_PADDING,\n style: {\n fill: textBackgroundColor,\n fillOpacity: 1,\n stroke: lineColor,\n lineWidth: 1,\n cornerRadius: DEFAULT_CORNER_RADIUS,\n },\n },\n }\n\n return [\n {\n type: 'type-step',\n autoRange: true,\n zIndex: ANNOTATION_Z_INDEX,\n connectDirection,\n expandDistance: useBracketStyle ? DEFAULT_BRACKET_EXPAND_DISTANCE : DEFAULT_EXPAND_DISTANCE,\n coordinates: (seriesData: any[], relativeSeries: ICartesianSeries) => {\n try {\n return [\n buildDifferenceCoordinateDatum({\n anchor: start,\n seriesData,\n relativeSeries,\n useElementStackEnd: usesRuntimeStackEnd,\n }),\n buildDifferenceCoordinateDatum({\n anchor: end,\n seriesData,\n relativeSeries,\n useElementStackEnd: usesRuntimeStackEnd,\n }),\n ]\n } catch {\n return []\n }\n },\n line: useBracketStyle\n ? {\n multiSegment: true,\n mainSegmentIndex: 1,\n style: [\n {\n visible: true,\n stroke: lineColor,\n lineWidth: DEFAULT_LINE_WIDTH,\n lineDash: DEFAULT_BRACKET_LINE_DASH,\n },\n {\n visible: true,\n stroke: lineColor,\n lineWidth: DEFAULT_LINE_WIDTH,\n },\n {\n visible: true,\n stroke: lineColor,\n lineWidth: DEFAULT_LINE_WIDTH,\n lineDash: DEFAULT_BRACKET_LINE_DASH,\n },\n ],\n }\n : {\n style: {\n visible: true,\n stroke: lineColor,\n lineWidth: DEFAULT_LINE_WIDTH,\n lineDash: [0],\n cornerRadius: DEFAULT_CORNER_RADIUS,\n },\n },\n label,\n startSymbol: {\n visible: false,\n },\n endSymbol: {\n visible: true,\n size: DEFAULT_END_SYMBOL_SIZE,\n refX: DEFAULT_END_SYMBOL_REF_X,\n style: {\n fill: lineColor,\n },\n },\n } as IMarkLineSpec,\n ]\n } catch {\n return []\n }\n })\n\n const specMarkLine = (chartSpec.markLine as IMarkLineSpec[]) || []\n\n return {\n ...spec,\n markLine: [...specMarkLine, ...markLine],\n }\n}\n"],"names":["DEFAULT_LINE_COLOR","DEFAULT_TEXT_COLOR","DEFAULT_TEXT_BACKGROUND_COLOR","DEFAULT_TEXT_FONT_SIZE","DEFAULT_EXPAND_DISTANCE","DEFAULT_LINE_WIDTH","DEFAULT_CORNER_RADIUS","DEFAULT_LABEL_PADDING","DEFAULT_END_SYMBOL_SIZE","DEFAULT_END_SYMBOL_REF_X","DEFAULT_BRACKET_EXPAND_DISTANCE","DEFAULT_BRACKET_LINE_DASH","getDifferenceLinePath","index","total","assertDifferenceLineConfig","value","path","Array","Error","start","end","annotationDifferenceLine","spec","context","advancedVSeed","vseed","theme","annotationDifferenceLineList","dataset","chartSpec","stackResolveMode","getDifferenceLineStackResolveMode","useElementStackEnd","usesDifferenceLineElementStackEnd","isBracketChart","markLine","resolveDifferenceAnchor","usesRuntimeStackEnd","useBracketStyle","isStackedBarElementBracket","connectDirection","inferDifferenceBracketDirection","inferDifferenceConnectDirection","lineColor","textColor","textBackgroundColor","textFontSize","differenceType","label","_markData","seriesData","buildDifferenceText","getRuntimeDifferenceValue","ANNOTATION_Z_INDEX","relativeSeries","buildDifferenceCoordinateDatum","specMarkLine"],"mappings":";;AAcA,MAAMA,qBAAqB;AAC3B,MAAMC,qBAAqB;AAC3B,MAAMC,gCAAgC;AACtC,MAAMC,yBAAyB;AAC/B,MAAMC,0BAA0B;AAChC,MAAMC,qBAAqB;AAC3B,MAAMC,wBAAwB;AAC9B,MAAMC,wBAAwB;AAC9B,MAAMC,0BAA0B;AAChC,MAAMC,2BAA2B;AACjC,MAAMC,kCAAkC;AACxC,MAAMC,4BAA8C;IAAC;IAAG;CAAE;AAE1D,MAAMC,wBAAwB,CAACC,OAAeC,QAC5CA,AAAU,MAAVA,QAAc,6BAA6B,CAAC,yBAAyB,EAAED,MAAM,CAAC,CAAC;AAEjF,MAAME,6BAA0G,CAC9GC,OACAC;IAEA,IAAI,AAAiB,YAAjB,OAAOD,SAAsBA,AAAU,SAAVA,SAAkBE,MAAM,OAAO,CAACF,QAC/D,MAAM,IAAIG,MAAM,GAAGF,KAAK,kBAAkB,CAAC;IAG7C,MAAMG,QAASJ,MAAkC,KAAK;IACtD,IAAI,AAAiB,YAAjB,OAAOI,SAAsBA,AAAU,SAAVA,SAAkBF,MAAM,OAAO,CAACE,QAC/D,MAAM,IAAID,MAAM,GAAGF,KAAK,kBAAkB,CAAC;IAE7C,IAAKG,AAA8C,QAA9CA,MAAkC,QAAQ,EAC7C,MAAM,IAAID,MAAM,GAAGF,KAAK,2BAA2B,CAAC;IAGtD,MAAMI,MAAOL,MAAkC,GAAG;IAClD,IAAI,AAAe,YAAf,OAAOK,OAAoBA,AAAQ,SAARA,OAAgBH,MAAM,OAAO,CAACG,MAC3D,MAAM,IAAIF,MAAM,GAAGF,KAAK,gBAAgB,CAAC;IAE3C,IAAKI,AAA4C,QAA5CA,IAAgC,QAAQ,EAC3C,MAAM,IAAIF,MAAM,GAAGF,KAAK,yBAAyB,CAAC;AAEtD;AAEO,MAAMK,oDAA2C,CAACC,MAAMC;IAC7D,MAAM,EAAEC,aAAa,EAAEC,KAAK,EAAE,GAAGF;IACjC,MAAMF,2BAA2BG,cAAc,UAAU,EAAE;IAE3D,IAAI,CAACH,0BACH,OAAOC;IAGT,MAAMI,QAAQF,cAAc,MAAM,EAAE,CAACC,MAAM,SAAS,CAAa,EAAE,YAAY;IAC/E,MAAME,+BAA+BV,MAAM,OAAO,CAACI,4BAC/CA,2BACA;QAACA;KAAyB;IAC9B,MAAMO,UAAUJ,cAAc,OAAO,CAAC,IAAI;IAC1C,MAAMK,YAAYP;IAClB,MAAMQ,mBAAmBC,kCAAkCN,OAAOD;IAClE,MAAMQ,qBAAqBC,kCAAkCR,OAAOD;IACpE,MAAMU,iBAAiBT,AAAoB,WAApBA,MAAM,SAAS,IAAeA,AAAoB,WAApBA,MAAM,SAAS;IAEpE,MAAMU,WAAWR,6BAA6B,OAAO,CAAC,CAACN,0BAA0BT;QAC/E,IAAI;YACFE,2BACEO,0BACAV,sBAAsBC,OAAOe,6BAA6B,MAAM;YAGlE,MAAMR,QAAQiB,wBAAwB;gBACpCR;gBACA,eAAe;gBACf,eAAeP,yBAAyB,KAAK,CAAC,QAAQ;gBACtD,MAAMQ;gBACNC;gBACA,uBAAuB,CAACE;YAC1B;YACA,MAAMZ,MAAMgB,wBAAwB;gBAClCR;gBACA,eAAe;gBACf,eAAeP,yBAAyB,GAAG,CAAC,QAAQ;gBACpD,MAAMQ;gBACNC;gBACA,uBAAuB,CAACE;YAC1B;YAEA,IAAI,CAACb,SAAS,CAACC,KACb,OAAO,EAAE;YAGX,IAAID,MAAM,IAAI,KAAKC,IAAI,IAAI,EACzB,OAAO,EAAE;YAGX,MAAMiB,sBACJL,sBACEP,AAAAA,CAAAA,AAAoB,aAApBA,MAAM,SAAS,IAAiBA,AAAoB,UAApBA,MAAM,SAAS,AAAS,KACxDN,AAAe,cAAfA,MAAM,IAAI,IACVW,AAAqB,WAArBA;YACJ,MAAMQ,kBACJJ,kBACET,AAAAA,CAAAA,AAAoB,aAApBA,MAAM,SAAS,IAAiBA,AAAoB,UAApBA,MAAM,SAAS,AAAS,KACxDN,AAAe,cAAfA,MAAM,IAAI,IACVW,AAAqB,WAArBA;YACJ,MAAMS,6BACJd,AAAoB,UAApBA,MAAM,SAAS,IAAcN,AAAe,cAAfA,MAAM,IAAI,IAAkBW,AAAqB,WAArBA;YAC3D,MAAMU,mBAAmBF,kBACrBC,6BACE,QACAE,gCAAgCtB,OAAOC,OACzCsB,gCAAgCjB,OAAO;gBAACN,MAAM,KAAK;gBAAEC,IAAI,KAAK;aAAC;YAEnE,MAAMuB,YAAYtB,yBAAyB,SAAS,IAAIK,OAAO,aAAa3B;YAC5E,MAAM6C,YAAYvB,yBAAyB,SAAS,IAAIK,OAAO,aAAa1B;YAC5E,MAAM6C,sBACJxB,yBAAyB,mBAAmB,IAAIK,OAAO,uBAAuBzB;YAChF,MAAM6C,eAAezB,yBAAyB,YAAY,IAAIK,OAAO,gBAAgBxB;YACrF,MAAM6C,iBAAiB1B,yBAAyB,cAAc,IAAI;YAElE,MAAM2B,QAAQX,sBACV;gBACE,SAAS;gBACT,UAAU;gBACV,MAAM;gBACN,cAAc,CAACY,WAAkBC;oBAC/B,IAAI;wBACF,OAAOC,oBACLC,0BAA0B;4BACxB,QAAQjC;4BACR+B;4BACA,oBAAoBb;wBACtB,IACAe,0BAA0B;4BACxB,QAAQhC;4BACR8B;4BACA,oBAAoBb;wBACtB,IACAU;oBAEJ,EAAE,OAAM;wBACN,OAAO;oBACT;gBACF;gBACA,OAAO;oBACL,MAAMH;oBACN,UAAUE;gBACZ;gBACA,iBAAiB;oBACf,SAAS;oBACT,SAASxC;oBACT,OAAO;wBACL,MAAMuC;wBACN,aAAa;wBACb,QAAQF;wBACR,WAAW;wBACX,cAActC;oBAChB;gBACF;YACF,IACA;gBACE,SAAS;gBACT,UAAU;gBACV,MAAM;gBACN,MAAM8C,oBAAoBhC,MAAM,KAAK,EAAEC,IAAI,KAAK,EAAE2B;gBAClD,OAAO;oBACL,MAAMH;oBACN,UAAUE;gBACZ;gBACA,iBAAiB;oBACf,SAAS;oBACT,SAASxC;oBACT,OAAO;wBACL,MAAMuC;wBACN,aAAa;wBACb,QAAQF;wBACR,WAAW;wBACX,cAActC;oBAChB;gBACF;YACF;YAEJ,OAAO;gBACL;oBACE,MAAM;oBACN,WAAW;oBACX,QAAQgD;oBACRb;oBACA,gBAAgBF,kBAAkB7B,kCAAkCN;oBACpE,aAAa,CAAC+C,YAAmBI;wBAC/B,IAAI;4BACF,OAAO;gCACLC,+BAA+B;oCAC7B,QAAQpC;oCACR+B;oCACAI;oCACA,oBAAoBjB;gCACtB;gCACAkB,+BAA+B;oCAC7B,QAAQnC;oCACR8B;oCACAI;oCACA,oBAAoBjB;gCACtB;6BACD;wBACH,EAAE,OAAM;4BACN,OAAO,EAAE;wBACX;oBACF;oBACA,MAAMC,kBACF;wBACE,cAAc;wBACd,kBAAkB;wBAClB,OAAO;4BACL;gCACE,SAAS;gCACT,QAAQK;gCACR,WAAWvC;gCACX,UAAUM;4BACZ;4BACA;gCACE,SAAS;gCACT,QAAQiC;gCACR,WAAWvC;4BACb;4BACA;gCACE,SAAS;gCACT,QAAQuC;gCACR,WAAWvC;gCACX,UAAUM;4BACZ;yBACD;oBACH,IACA;wBACE,OAAO;4BACL,SAAS;4BACT,QAAQiC;4BACR,WAAWvC;4BACX,UAAU;gCAAC;6BAAE;4BACb,cAAcC;wBAChB;oBACF;oBACJ2C;oBACA,aAAa;wBACX,SAAS;oBACX;oBACA,WAAW;wBACT,SAAS;wBACT,MAAMzC;wBACN,MAAMC;wBACN,OAAO;4BACL,MAAMmC;wBACR;oBACF;gBACF;aACD;QACH,EAAE,OAAM;YACN,OAAO,EAAE;QACX;IACF;IAEA,MAAMa,eAAgB3B,UAAU,QAAQ,IAAwB,EAAE;IAElE,OAAO;QACL,GAAGP,IAAI;QACP,UAAU;eAAIkC;eAAiBrB;SAAS;IAC1C;AACF"}
|
|
1
|
+
{"version":3,"file":"pipeline/spec/chart/pipes/annotation/annotationDifferenceLine.js","sources":["../../../../../../../src/pipeline/spec/chart/pipes/annotation/annotationDifferenceLine.ts"],"sourcesContent":["import type { IAreaChartSpec, IBarChartSpec, ICartesianSeries, ILineChartSpec, IMarkLineSpec } from '@visactor/vchart'\nimport type { AnnotationDifferenceLine, Measure, VChartSpecPipe } from 'src/types'\nimport { createFormatter, findAllMeasures, autoFormatter } from '../../../../utils'\nimport {\n isDimensionSelector,\n isFieldSelector,\n isMeasureSelector,\n isPartialDatumSelector,\n} from '../../../../../dataSelector'\nimport { isEmpty } from 'remeda'\nimport { ANNOTATION_Z_INDEX } from '../../../../utils/constant'\nimport {\n buildDifferenceCoordinateDatum,\n buildDifferenceText,\n getDifferenceLineStackResolveMode,\n getRuntimeDifferenceValue,\n inferDifferenceBracketDirection,\n inferDifferenceConnectDirection,\n type ResolvedDifferenceAnchor,\n usesDifferenceLineElementStackEnd,\n resolveDifferenceAnchor,\n} from './annotationDifferenceLineCommon'\n\nconst DEFAULT_LINE_COLOR = '#BCC1CB'\nconst DEFAULT_TEXT_COLOR = '#ffffff'\nconst DEFAULT_TEXT_BACKGROUND_COLOR = '#BCC1CB'\nconst DEFAULT_TEXT_FONT_SIZE = 12\nconst DEFAULT_EXPAND_DISTANCE = 24\nconst DEFAULT_LINE_WIDTH = 2\nconst DEFAULT_CORNER_RADIUS = 4\nconst DEFAULT_LABEL_PADDING = 4\nconst DEFAULT_END_SYMBOL_SIZE = 12\nconst DEFAULT_END_SYMBOL_REF_X = -4\nconst DEFAULT_BRACKET_EXPAND_DISTANCE = 80\nconst DEFAULT_BRACKET_LINE_DASH: [number, number] = [2, 2]\nconst DEFAULT_PERCENT_DIFFERENCE_FORMAT = {\n type: 'percent' as const,\n fractionDigits: 2,\n}\n\nconst getDifferenceLinePath = (index: number, total: number) =>\n total === 1 ? 'annotationDifferenceLine' : `annotationDifferenceLine[${index}]`\n\nconst toArray = <T>(value: T | T[] | undefined | null): T[] => {\n if (Array.isArray(value)) {\n return value\n }\n\n return value === undefined || value === null ? [] : [value]\n}\n\nconst getAxisFormatter = (spec: IBarChartSpec | ILineChartSpec | IAreaChartSpec) => {\n const valueAxisOrient = spec.direction === 'horizontal' ? 'bottom' : 'left'\n const formatMethod = spec.axes?.find((axis) => axis.orient === valueAxisOrient)?.label?.formatMethod\n\n return typeof formatMethod === 'function'\n ? (value: number) => String(formatMethod(value as never) ?? value)\n : undefined\n}\n\nconst getExplicitMeasureFormat = (measure?: Measure) => {\n if (measure?.numFormat && !isEmpty(measure.numFormat)) {\n return measure.numFormat\n }\n\n if (measure?.format && !isEmpty(measure.format)) {\n return measure.format\n }\n\n return undefined\n}\n\nconst inferMeasureIdFromDatum = (anchor: ResolvedDifferenceAnchor, measureIds: string[]) => {\n const candidateValues = [anchor.matchedDatum, anchor.coordinateDatum]\n\n for (const datum of candidateValues) {\n if (!datum) {\n continue\n }\n\n const candidates = measureIds.filter((measureId) => Number(datum[measureId]) === anchor.value)\n\n if (candidates.length === 1) {\n return candidates[0]\n }\n }\n\n return undefined\n}\n\nconst inferMeasureIdFromSelector = (\n selectorValue: AnnotationDifferenceLine['start']['selector'],\n measureIdSet: Set<string>,\n) => {\n const matchedMeasureIds = new Set<string>()\n\n for (const currentSelector of toArray(selectorValue)) {\n if (isMeasureSelector(currentSelector)) {\n if (measureIdSet.has(currentSelector.field)) {\n matchedMeasureIds.add(currentSelector.field)\n }\n continue\n }\n\n if (isFieldSelector(currentSelector) || isDimensionSelector(currentSelector)) {\n continue\n }\n\n if (isPartialDatumSelector(currentSelector)) {\n Object.keys(currentSelector).forEach((field) => {\n if (measureIdSet.has(field)) {\n matchedMeasureIds.add(field)\n }\n })\n }\n }\n\n return matchedMeasureIds.size === 1 ? Array.from(matchedMeasureIds)[0] : undefined\n}\n\nconst resolveDifferenceMeasureId = (\n anchor: ResolvedDifferenceAnchor,\n selectorValue: AnnotationDifferenceLine['start']['selector'],\n measureIds: string[],\n) => {\n if (anchor.mode !== 'element') {\n return undefined\n }\n\n if (measureIds.length === 1) {\n return measureIds[0]\n }\n\n const measureIdSet = new Set(measureIds)\n return inferMeasureIdFromSelector(selectorValue, measureIdSet) ?? inferMeasureIdFromDatum(anchor, measureIds)\n}\n\nconst assertDifferenceLineConfig: (value: unknown, path: string) => asserts value is AnnotationDifferenceLine = (\n value,\n path,\n) => {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) {\n throw new Error(`${path} must be an object`)\n }\n\n const start = (value as Record<string, unknown>).start\n if (typeof start !== 'object' || start === null || Array.isArray(start)) {\n throw new Error(`${path}.start is required`)\n }\n if ((start as Record<string, unknown>).selector == null) {\n throw new Error(`${path}.start.selector is required`)\n }\n\n const end = (value as Record<string, unknown>).end\n if (typeof end !== 'object' || end === null || Array.isArray(end)) {\n throw new Error(`${path}.end is required`)\n }\n if ((end as Record<string, unknown>).selector == null) {\n throw new Error(`${path}.end.selector is required`)\n }\n}\n\nexport const annotationDifferenceLine: VChartSpecPipe = (spec, context) => {\n const { advancedVSeed, vseed } = context\n const annotationDifferenceLine = advancedVSeed.annotation?.annotationDifferenceLine\n\n if (!annotationDifferenceLine) {\n return spec\n }\n\n const theme = advancedVSeed.config?.[vseed.chartType as 'column']?.annotation?.annotationDifferenceLine\n const annotationDifferenceLineList = Array.isArray(annotationDifferenceLine)\n ? annotationDifferenceLine\n : [annotationDifferenceLine]\n const dataset = advancedVSeed.dataset.flat()\n const chartSpec = spec as IBarChartSpec | ILineChartSpec | IAreaChartSpec\n const stackResolveMode = getDifferenceLineStackResolveMode(vseed, advancedVSeed)\n const useElementStackEnd = usesDifferenceLineElementStackEnd(vseed, advancedVSeed)\n const isBracketChart = vseed.chartType === 'line' || vseed.chartType === 'area'\n const measures = findAllMeasures(advancedVSeed.measures)\n const measureIds = measures.map((measure) => measure.id)\n const axisFormatter = getAxisFormatter(chartSpec)\n const percentFormatter = createFormatter(DEFAULT_PERCENT_DIFFERENCE_FORMAT)\n\n const markLine = annotationDifferenceLineList.flatMap((annotationDifferenceLine, index) => {\n try {\n assertDifferenceLineConfig(\n annotationDifferenceLine,\n getDifferenceLinePath(index, annotationDifferenceLineList.length),\n )\n\n const start = resolveDifferenceAnchor({\n dataset,\n selectorLabel: 'start',\n selectorValue: annotationDifferenceLine.start.selector,\n spec: chartSpec,\n stackResolveMode,\n allowSelectorFallback: !useElementStackEnd,\n })\n const end = resolveDifferenceAnchor({\n dataset,\n selectorLabel: 'end',\n selectorValue: annotationDifferenceLine.end.selector,\n spec: chartSpec,\n stackResolveMode,\n allowSelectorFallback: !useElementStackEnd,\n })\n\n if (!start || !end) {\n return []\n }\n\n if (start.mode !== end.mode) {\n return []\n }\n\n const usesRuntimeStackEnd =\n useElementStackEnd ||\n ((vseed.chartType === 'column' || vseed.chartType === 'bar') &&\n start.mode === 'element' &&\n stackResolveMode === 'auto')\n const useBracketStyle =\n isBracketChart ||\n ((vseed.chartType === 'column' || vseed.chartType === 'bar') &&\n start.mode === 'element' &&\n stackResolveMode === 'auto')\n const isStackedBarElementBracket =\n vseed.chartType === 'bar' && start.mode === 'element' && stackResolveMode === 'auto'\n const connectDirection = useBracketStyle\n ? isStackedBarElementBracket\n ? 'top'\n : inferDifferenceBracketDirection(start, end)\n : inferDifferenceConnectDirection(vseed, [start.value, end.value])\n\n const lineColor = annotationDifferenceLine.lineColor ?? theme?.lineColor ?? DEFAULT_LINE_COLOR\n const textColor = annotationDifferenceLine.textColor ?? theme?.textColor ?? DEFAULT_TEXT_COLOR\n const textBackgroundColor =\n annotationDifferenceLine.textBackgroundColor ?? theme?.textBackgroundColor ?? DEFAULT_TEXT_BACKGROUND_COLOR\n const textFontSize = annotationDifferenceLine.textFontSize ?? theme?.textFontSize ?? DEFAULT_TEXT_FONT_SIZE\n const differenceType = annotationDifferenceLine.differenceType ?? 'absolute'\n const startMeasureId = resolveDifferenceMeasureId(start, annotationDifferenceLine.start.selector, measureIds)\n const endMeasureId = resolveDifferenceMeasureId(end, annotationDifferenceLine.end.selector, measureIds)\n const sameMeasure = startMeasureId !== undefined && startMeasureId === endMeasureId\n const explicitMeasureFormat = sameMeasure\n ? getExplicitMeasureFormat(measures.find((measure) => measure.id === startMeasureId))\n : undefined\n const differenceFormatter =\n differenceType === 'percent'\n ? percentFormatter\n : explicitMeasureFormat\n ? createFormatter(explicitMeasureFormat)\n : (axisFormatter ?? autoFormatter)\n\n const label = usesRuntimeStackEnd\n ? {\n visible: true,\n position: 'middle',\n refY: 0,\n formatMethod: (_markData: any[], seriesData: any[]) => {\n try {\n return buildDifferenceText(\n getRuntimeDifferenceValue({\n anchor: start,\n seriesData,\n useElementStackEnd: usesRuntimeStackEnd,\n }),\n getRuntimeDifferenceValue({\n anchor: end,\n seriesData,\n useElementStackEnd: usesRuntimeStackEnd,\n }),\n differenceType,\n differenceFormatter,\n )\n } catch {\n return ''\n }\n },\n style: {\n fill: textColor,\n fontSize: textFontSize,\n },\n labelBackground: {\n visible: true,\n padding: DEFAULT_LABEL_PADDING,\n style: {\n fill: textBackgroundColor,\n fillOpacity: 1,\n stroke: lineColor,\n lineWidth: 1,\n cornerRadius: DEFAULT_CORNER_RADIUS,\n },\n },\n }\n : {\n visible: true,\n position: 'middle',\n refY: 0,\n text: buildDifferenceText(start.value, end.value, differenceType, differenceFormatter),\n style: {\n fill: textColor,\n fontSize: textFontSize,\n },\n labelBackground: {\n visible: true,\n padding: DEFAULT_LABEL_PADDING,\n style: {\n fill: textBackgroundColor,\n fillOpacity: 1,\n stroke: lineColor,\n lineWidth: 1,\n cornerRadius: DEFAULT_CORNER_RADIUS,\n },\n },\n }\n\n return [\n {\n type: 'type-step',\n autoRange: true,\n zIndex: ANNOTATION_Z_INDEX,\n connectDirection,\n expandDistance: useBracketStyle ? DEFAULT_BRACKET_EXPAND_DISTANCE : DEFAULT_EXPAND_DISTANCE,\n coordinates: (seriesData: any[], relativeSeries: ICartesianSeries) => {\n try {\n return [\n buildDifferenceCoordinateDatum({\n anchor: start,\n seriesData,\n relativeSeries,\n useElementStackEnd: usesRuntimeStackEnd,\n }),\n buildDifferenceCoordinateDatum({\n anchor: end,\n seriesData,\n relativeSeries,\n useElementStackEnd: usesRuntimeStackEnd,\n }),\n ]\n } catch {\n return []\n }\n },\n line: useBracketStyle\n ? {\n multiSegment: true,\n mainSegmentIndex: 1,\n style: [\n {\n visible: true,\n stroke: lineColor,\n lineWidth: DEFAULT_LINE_WIDTH,\n lineDash: DEFAULT_BRACKET_LINE_DASH,\n },\n {\n visible: true,\n stroke: lineColor,\n lineWidth: DEFAULT_LINE_WIDTH,\n },\n {\n visible: true,\n stroke: lineColor,\n lineWidth: DEFAULT_LINE_WIDTH,\n lineDash: DEFAULT_BRACKET_LINE_DASH,\n },\n ],\n }\n : {\n style: {\n visible: true,\n stroke: lineColor,\n lineWidth: DEFAULT_LINE_WIDTH,\n lineDash: [0],\n cornerRadius: DEFAULT_CORNER_RADIUS,\n },\n },\n label,\n startSymbol: {\n visible: false,\n },\n endSymbol: {\n visible: true,\n size: DEFAULT_END_SYMBOL_SIZE,\n refX: DEFAULT_END_SYMBOL_REF_X,\n style: {\n fill: lineColor,\n },\n },\n } as IMarkLineSpec,\n ]\n } catch {\n return []\n }\n })\n\n const specMarkLine = (chartSpec.markLine as IMarkLineSpec[]) || []\n\n return {\n ...spec,\n markLine: [...specMarkLine, ...markLine],\n }\n}\n"],"names":["DEFAULT_LINE_COLOR","DEFAULT_TEXT_COLOR","DEFAULT_TEXT_BACKGROUND_COLOR","DEFAULT_TEXT_FONT_SIZE","DEFAULT_EXPAND_DISTANCE","DEFAULT_LINE_WIDTH","DEFAULT_CORNER_RADIUS","DEFAULT_LABEL_PADDING","DEFAULT_END_SYMBOL_SIZE","DEFAULT_END_SYMBOL_REF_X","DEFAULT_BRACKET_EXPAND_DISTANCE","DEFAULT_BRACKET_LINE_DASH","DEFAULT_PERCENT_DIFFERENCE_FORMAT","getDifferenceLinePath","index","total","toArray","value","Array","getAxisFormatter","spec","valueAxisOrient","formatMethod","axis","String","undefined","getExplicitMeasureFormat","measure","isEmpty","inferMeasureIdFromDatum","anchor","measureIds","candidateValues","datum","candidates","measureId","Number","inferMeasureIdFromSelector","selectorValue","measureIdSet","matchedMeasureIds","Set","currentSelector","isMeasureSelector","isFieldSelector","isDimensionSelector","isPartialDatumSelector","Object","field","resolveDifferenceMeasureId","assertDifferenceLineConfig","path","Error","start","end","annotationDifferenceLine","context","advancedVSeed","vseed","theme","annotationDifferenceLineList","dataset","chartSpec","stackResolveMode","getDifferenceLineStackResolveMode","useElementStackEnd","usesDifferenceLineElementStackEnd","isBracketChart","measures","findAllMeasures","axisFormatter","percentFormatter","createFormatter","markLine","resolveDifferenceAnchor","usesRuntimeStackEnd","useBracketStyle","isStackedBarElementBracket","connectDirection","inferDifferenceBracketDirection","inferDifferenceConnectDirection","lineColor","textColor","textBackgroundColor","textFontSize","differenceType","startMeasureId","endMeasureId","sameMeasure","explicitMeasureFormat","differenceFormatter","autoFormatter","label","_markData","seriesData","buildDifferenceText","getRuntimeDifferenceValue","ANNOTATION_Z_INDEX","relativeSeries","buildDifferenceCoordinateDatum","specMarkLine"],"mappings":"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"}
|
|
@@ -36,7 +36,8 @@ export declare const getRuntimeDifferenceValue: (options: {
|
|
|
36
36
|
seriesData: Datum[];
|
|
37
37
|
useElementStackEnd?: boolean;
|
|
38
38
|
}) => number;
|
|
39
|
-
export declare const
|
|
39
|
+
export declare const getDifferenceValue: (startValue: number, endValue: number, differenceType?: "absolute" | "percent") => number;
|
|
40
|
+
export declare const buildDifferenceText: (startValue: number, endValue: number, differenceType?: "absolute" | "percent", formatter?: (value: number) => string) => string;
|
|
40
41
|
export declare const inferDifferenceConnectDirection: (vseed: VSeed, values: [number, number]) => "left" | "top" | "right" | "bottom";
|
|
41
42
|
export declare const inferDifferenceBracketDirection: (start: ResolvedDifferenceAnchor, end: ResolvedDifferenceAnchor) => "left" | "right";
|
|
42
43
|
export {};
|
|
@@ -157,8 +157,9 @@ const resolveDifferenceAnchor = (options)=>{
|
|
|
157
157
|
};
|
|
158
158
|
};
|
|
159
159
|
const getStackRuntimeTotal = (runtimeMatches)=>{
|
|
160
|
+
if (0 === runtimeMatches.length) return;
|
|
160
161
|
const stackEndValues = runtimeMatches.map((datum)=>Number(datum[STACK_END_FIELD]));
|
|
161
|
-
if (stackEndValues.some((value)
|
|
162
|
+
if (stackEndValues.some((value)=>!Number.isFinite(value))) return;
|
|
162
163
|
if (hasMixedSigns(stackEndValues)) throw new Error('annotationDifferenceLine does not support mixed-sign runtime stack totals in v1');
|
|
163
164
|
return stackEndValues.some((value)=>value < 0) ? Math.min(...stackEndValues) : Math.max(...stackEndValues);
|
|
164
165
|
};
|
|
@@ -171,7 +172,7 @@ const buildDifferenceCoordinateDatum = (options)=>{
|
|
|
171
172
|
const runtimeStackEnd = Number(runtimeMatch?.[STACK_END_FIELD]);
|
|
172
173
|
return {
|
|
173
174
|
...runtimeMatch ?? anchor.coordinateDatum,
|
|
174
|
-
[relativeSeries.getStackValueField()]: Number.
|
|
175
|
+
[relativeSeries.getStackValueField()]: Number.isFinite(runtimeStackEnd) ? runtimeStackEnd : anchor.value
|
|
175
176
|
};
|
|
176
177
|
}
|
|
177
178
|
const runtimeMatches = seriesData.filter((datum)=>isSubset(anchor.stackGroupDatum, datum));
|
|
@@ -190,14 +191,20 @@ const getRuntimeDifferenceValue = (options)=>{
|
|
|
190
191
|
if (!useElementStackEnd || !anchor.matchedDatum) return anchor.value;
|
|
191
192
|
const runtimeMatch = seriesData.find((datum)=>isSubset(anchor.matchedDatum, datum));
|
|
192
193
|
const runtimeStackEnd = Number(runtimeMatch?.[STACK_END_FIELD]);
|
|
193
|
-
return Number.
|
|
194
|
+
return Number.isFinite(runtimeStackEnd) ? runtimeStackEnd : anchor.value;
|
|
194
195
|
};
|
|
195
|
-
const
|
|
196
|
+
const getDifferenceValue = (startValue, endValue, differenceType = 'absolute')=>{
|
|
196
197
|
if ('percent' === differenceType) {
|
|
197
198
|
if (0 === startValue) throw new Error('annotationDifferenceLine percent difference cannot be computed because start value is 0');
|
|
198
|
-
return
|
|
199
|
+
return (endValue - startValue) / startValue;
|
|
199
200
|
}
|
|
200
|
-
return
|
|
201
|
+
return endValue - startValue;
|
|
202
|
+
};
|
|
203
|
+
const buildDifferenceText = (startValue, endValue, differenceType = 'absolute', formatter)=>{
|
|
204
|
+
const differenceValue = getDifferenceValue(startValue, endValue, differenceType);
|
|
205
|
+
if (formatter) return formatter(differenceValue);
|
|
206
|
+
if ('percent' === differenceType) return `${(100 * differenceValue).toFixed(2)}%`;
|
|
207
|
+
return `${differenceValue}`;
|
|
201
208
|
};
|
|
202
209
|
const inferDifferenceConnectDirection = (vseed, values)=>{
|
|
203
210
|
const isNegativeSide = values.every((value)=>value < 0);
|
|
@@ -208,6 +215,6 @@ const inferDifferenceBracketDirection = (start, end)=>{
|
|
|
208
215
|
if (void 0 === start.bandIndex || void 0 === end.bandIndex) return 'right';
|
|
209
216
|
return start.bandIndex <= end.bandIndex ? 'right' : 'left';
|
|
210
217
|
};
|
|
211
|
-
export { buildDifferenceCoordinateDatum, buildDifferenceText, getDifferenceLineStackResolveMode, getRuntimeDifferenceValue, getStackRuntimeTotal, inferDifferenceBracketDirection, inferDifferenceConnectDirection, resolveDifferenceAnchor, usesDifferenceLineElementStackEnd };
|
|
218
|
+
export { buildDifferenceCoordinateDatum, buildDifferenceText, getDifferenceLineStackResolveMode, getDifferenceValue, getRuntimeDifferenceValue, getStackRuntimeTotal, inferDifferenceBracketDirection, inferDifferenceConnectDirection, resolveDifferenceAnchor, usesDifferenceLineElementStackEnd };
|
|
212
219
|
|
|
213
220
|
//# sourceMappingURL=annotationDifferenceLineCommon.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pipeline/spec/chart/pipes/annotation/annotationDifferenceLineCommon.js","sources":["../../../../../../../src/pipeline/spec/chart/pipes/annotation/annotationDifferenceLineCommon.ts"],"sourcesContent":["import type { IAreaChartSpec, IBarChartSpec, ICartesianSeries, ILineChartSpec } from '@visactor/vchart'\nimport {\n isDimensionSelector,\n isFieldSelector,\n isMeasureSelector,\n isPartialDatumSelector,\n isValueSelector,\n selector,\n} from '../../../../../dataSelector'\nimport { hasMultipleMeasureInSingleView, isBarLikeChart } from 'src/pipeline/utils'\nimport type { AdvancedVSeed, Datum, Selector, Selectors, VSeed } from 'src/types'\nimport { isSubset } from './utils'\n\ntype DifferenceSelectorLabel = 'start' | 'end'\ntype DifferenceChartSpec = IBarChartSpec | ILineChartSpec | IAreaChartSpec\nexport type DifferenceStackResolveMode = 'none' | 'stackTotal' | 'auto'\nexport type DifferenceAnchorMode = 'element' | 'stackTotal'\n\nexport type ResolvedDifferenceAnchor = {\n mode: DifferenceAnchorMode\n selectorLabel: DifferenceSelectorLabel\n coordinateDatum: Datum\n matchedDatum?: Datum\n stackGroupDatum?: Datum\n bandDatum: Datum\n bandIndex?: number\n value: number\n}\n\nconst STACK_END_FIELD = '__VCHART_STACK_END'\n\nconst toArray = <T>(value: T | T[] | undefined | null): T[] => {\n if (Array.isArray(value)) {\n return value\n }\n\n return value === undefined || value === null ? [] : [value]\n}\n\nconst getDifferenceBandFields = (spec: DifferenceChartSpec): string[] => {\n return toArray((spec.direction === 'horizontal' ? spec.yField : spec.xField) as string | string[] | undefined)\n}\n\nconst getDifferenceValueField = (spec: DifferenceChartSpec): string => {\n const valueField = toArray(\n (spec.direction === 'horizontal' ? spec.xField : spec.yField) as string | string[] | undefined,\n )[0]\n\n if (!valueField) {\n throw new Error('annotationDifferenceLine requires a value field in the target chart spec')\n }\n\n return valueField\n}\n\nconst normalizeDifferenceValue = (value: unknown, selectorLabel: DifferenceSelectorLabel, field: string): number => {\n const numberValue = Number(value)\n\n if (Number.isNaN(numberValue)) {\n throw new Error(\n `annotationDifferenceLine ${selectorLabel} selector resolved to a non-numeric value on field \"${field}\"`,\n )\n }\n\n return numberValue\n}\n\nconst buildStackGroupDatum = (datum: Datum, bandFields: string[]) => {\n return Object.fromEntries(bandFields.map((field) => [field, datum[field]])) as Datum\n}\n\nconst resolveBandIndex = (dataset: Datum[], bandDatum: Datum) => {\n const bandIndex = dataset.findIndex((datum) => isSubset(bandDatum, datum))\n\n return bandIndex >= 0 ? bandIndex : undefined\n}\n\nconst buildFallbackSelectorDatum = (selectorValue: Selector | Selectors): Datum | undefined => {\n const selectorList = Array.isArray(selectorValue) ? selectorValue : [selectorValue]\n const fallbackDatum: Datum = {}\n\n for (const currentSelector of selectorList) {\n if (isValueSelector(currentSelector) || isFieldSelector(currentSelector)) {\n return undefined\n }\n\n if (isMeasureSelector(currentSelector)) {\n const operator = currentSelector.operator || currentSelector.op\n const selectorValues = Array.isArray(currentSelector.value) ? currentSelector.value : [currentSelector.value]\n\n if (!['=', '=='].includes(operator as string) || selectorValues.length !== 1) {\n return undefined\n }\n\n fallbackDatum[currentSelector.field] = selectorValues[0]\n continue\n }\n\n if (isDimensionSelector(currentSelector)) {\n const operator = currentSelector.operator || currentSelector.op\n const selectorValues = Array.isArray(currentSelector.value) ? currentSelector.value : [currentSelector.value]\n\n if (operator !== 'in' || selectorValues.length !== 1) {\n return undefined\n }\n\n fallbackDatum[currentSelector.field] = selectorValues[0]\n continue\n }\n\n if (isPartialDatumSelector(currentSelector)) {\n Object.assign(fallbackDatum, currentSelector)\n continue\n }\n\n return undefined\n }\n\n return Object.keys(fallbackDatum).length > 0 ? fallbackDatum : undefined\n}\n\nconst inferFallbackValue = (fallbackDatum: Datum, valueField: string, bandFields: string[]) => {\n if (valueField in fallbackDatum) {\n return fallbackDatum[valueField]\n }\n\n const numericCandidateKeys = Object.keys(fallbackDatum).filter((key) => {\n if (key === valueField || bandFields.includes(key)) {\n return false\n }\n\n return typeof fallbackDatum[key] === 'number'\n })\n\n if (numericCandidateKeys.length !== 1) {\n return undefined\n }\n\n return fallbackDatum[numericCandidateKeys[0]]\n}\n\nconst resolveFallbackAnchor = (options: {\n dataset: Datum[]\n selectorLabel: DifferenceSelectorLabel\n selectorValue: Selector | Selectors\n valueField: string\n bandFields: string[]\n}): ResolvedDifferenceAnchor | undefined => {\n const { dataset, selectorLabel, selectorValue, valueField, bandFields } = options\n const fallbackDatum = buildFallbackSelectorDatum(selectorValue)\n\n if (!fallbackDatum) {\n return undefined\n }\n\n const inferredValue = inferFallbackValue(fallbackDatum, valueField, bandFields)\n\n if (inferredValue === undefined) {\n return undefined\n }\n\n const coordinateDatum = {\n ...fallbackDatum,\n [valueField]: inferredValue,\n }\n const bandDatum = buildStackGroupDatum(coordinateDatum, bandFields)\n\n try {\n return {\n mode: 'element',\n selectorLabel,\n coordinateDatum,\n matchedDatum: fallbackDatum,\n bandDatum,\n bandIndex: resolveBandIndex(dataset, bandDatum),\n value: normalizeDifferenceValue(inferredValue, selectorLabel, valueField),\n }\n } catch {\n return undefined\n }\n}\n\nconst hasMixedSigns = (values: number[]) => {\n const nonZeroValues = values.filter((value) => value !== 0)\n\n return nonZeroValues.some((value) => value > 0) && nonZeroValues.some((value) => value < 0)\n}\n\nexport const getDifferenceLineStackResolveMode = (\n vseed: VSeed,\n advancedVSeed: AdvancedVSeed,\n): DifferenceStackResolveMode => {\n const hasMultipleMeasure = hasMultipleMeasureInSingleView(advancedVSeed.reshapeMeasures ?? [])\n\n if (!hasMultipleMeasure) {\n return 'none'\n }\n\n if (vseed.chartType === 'column') {\n return 'auto'\n }\n\n if (vseed.chartType === 'bar') {\n return 'auto'\n }\n\n return 'none'\n}\n\nexport const usesDifferenceLineElementStackEnd = (vseed: VSeed, advancedVSeed: AdvancedVSeed) => {\n return vseed.chartType === 'area' && hasMultipleMeasureInSingleView(advancedVSeed.reshapeMeasures ?? [])\n}\n\nexport const resolveDifferenceAnchor = (options: {\n dataset: Datum[]\n selectorLabel: DifferenceSelectorLabel\n selectorValue: Selector | Selectors\n spec: DifferenceChartSpec\n stackResolveMode: DifferenceStackResolveMode\n allowSelectorFallback?: boolean\n}): ResolvedDifferenceAnchor | undefined => {\n const { dataset, selectorLabel, selectorValue, spec, stackResolveMode, allowSelectorFallback = true } = options\n const matches = dataset.filter((datum) => selector(datum, selectorValue))\n const valueField = getDifferenceValueField(spec)\n const bandFields = getDifferenceBandFields(spec)\n\n if (matches.length === 0) {\n return stackResolveMode === 'none' && allowSelectorFallback\n ? resolveFallbackAnchor({ dataset, selectorLabel, selectorValue, valueField, bandFields })\n : undefined\n }\n\n if (stackResolveMode === 'none' || (stackResolveMode === 'auto' && matches.length === 1)) {\n if (matches.length !== 1) {\n throw new Error(\n `annotationDifferenceLine ${selectorLabel} selector must resolve to exactly one datum, got ${matches.length}`,\n )\n }\n\n const bandDatum = buildStackGroupDatum(matches[0], bandFields)\n\n return {\n mode: 'element',\n selectorLabel,\n coordinateDatum: matches[0],\n matchedDatum: matches[0],\n bandDatum,\n bandIndex: resolveBandIndex(dataset, bandDatum),\n value: normalizeDifferenceValue(matches[0][valueField], selectorLabel, valueField),\n }\n }\n const stackGroups = new Map<string, Datum>()\n\n matches.forEach((datum) => {\n const stackGroupDatum = buildStackGroupDatum(datum, bandFields)\n stackGroups.set(JSON.stringify(stackGroupDatum), stackGroupDatum)\n })\n\n if (stackGroups.size !== 1) {\n throw new Error(\n `annotationDifferenceLine ${selectorLabel} selector must resolve to exactly one stack group, got ${stackGroups.size}`,\n )\n }\n\n const stackGroupDatum = Array.from(stackGroups.values())[0]\n const groupRows = dataset.filter((datum) => isSubset(stackGroupDatum, datum))\n\n if (stackResolveMode === 'auto' && groupRows.length !== matches.length) {\n throw new Error(\n `annotationDifferenceLine ${selectorLabel} selector must resolve to exactly one stack element or one full stack group`,\n )\n }\n const groupValues = groupRows.map((datum) => normalizeDifferenceValue(datum[valueField], selectorLabel, valueField))\n\n if (hasMixedSigns(groupValues)) {\n throw new Error('annotationDifferenceLine does not support mixed-sign stack totals in v1')\n }\n\n return {\n mode: 'stackTotal',\n selectorLabel,\n coordinateDatum: stackGroupDatum,\n matchedDatum: matches[0],\n stackGroupDatum,\n bandDatum: stackGroupDatum,\n bandIndex: resolveBandIndex(dataset, stackGroupDatum),\n value: groupValues.reduce((sum, value) => sum + value, 0),\n }\n}\n\nexport const getStackRuntimeTotal = (runtimeMatches: Datum[]) => {\n const stackEndValues = runtimeMatches.map((datum) => Number(datum[STACK_END_FIELD]))\n\n if (stackEndValues.some((value) => Number.isNaN(value))) {\n return undefined\n }\n\n if (hasMixedSigns(stackEndValues)) {\n throw new Error('annotationDifferenceLine does not support mixed-sign runtime stack totals in v1')\n }\n\n return stackEndValues.some((value) => value < 0) ? Math.min(...stackEndValues) : Math.max(...stackEndValues)\n}\n\nexport const buildDifferenceCoordinateDatum = (options: {\n anchor: ResolvedDifferenceAnchor\n seriesData: Datum[]\n relativeSeries: ICartesianSeries\n useElementStackEnd?: boolean\n}) => {\n const { anchor, seriesData, relativeSeries, useElementStackEnd = false } = options\n\n if (!anchor.stackGroupDatum) {\n const runtimeMatches = anchor.matchedDatum\n ? seriesData.filter((datum) => isSubset(anchor.matchedDatum as Datum, datum))\n : []\n\n if (!useElementStackEnd) {\n return runtimeMatches[0] ?? anchor.coordinateDatum\n }\n\n const runtimeMatch = runtimeMatches[0]\n const runtimeStackEnd = Number(runtimeMatch?.[STACK_END_FIELD])\n\n return {\n ...(runtimeMatch ?? anchor.coordinateDatum),\n [relativeSeries.getStackValueField()]: Number.isNaN(runtimeStackEnd) ? anchor.value : runtimeStackEnd,\n }\n }\n\n const runtimeMatches = seriesData.filter((datum) => isSubset(anchor.stackGroupDatum as Datum, datum))\n const runtimeStackTotal = getStackRuntimeTotal(runtimeMatches)\n\n return {\n ...(runtimeMatches[0] ?? anchor.coordinateDatum),\n [relativeSeries.getStackValueField()]: runtimeStackTotal ?? anchor.value,\n }\n}\n\nexport const getRuntimeDifferenceValue = (options: {\n anchor: ResolvedDifferenceAnchor\n seriesData: Datum[]\n useElementStackEnd?: boolean\n}) => {\n const { anchor, seriesData, useElementStackEnd = false } = options\n\n if (anchor.stackGroupDatum) {\n const runtimeMatches = seriesData.filter((datum) => isSubset(anchor.stackGroupDatum as Datum, datum))\n return getStackRuntimeTotal(runtimeMatches) ?? anchor.value\n }\n\n if (!useElementStackEnd || !anchor.matchedDatum) {\n return anchor.value\n }\n\n const runtimeMatch = seriesData.find((datum) => isSubset(anchor.matchedDatum as Datum, datum))\n const runtimeStackEnd = Number(runtimeMatch?.[STACK_END_FIELD])\n\n return Number.isNaN(runtimeStackEnd) ? anchor.value : runtimeStackEnd\n}\n\nexport const buildDifferenceText = (\n startValue: number,\n endValue: number,\n differenceType: 'absolute' | 'percent' = 'absolute',\n) => {\n if (differenceType === 'percent') {\n if (startValue === 0) {\n throw new Error('annotationDifferenceLine percent difference cannot be computed because start value is 0')\n }\n\n return `${(((endValue - startValue) / startValue) * 100).toFixed(0)}%`\n }\n\n return `${endValue - startValue}`\n}\n\nexport const inferDifferenceConnectDirection = (vseed: VSeed, values: [number, number]) => {\n const isNegativeSide = values.every((value) => value < 0)\n\n if (isBarLikeChart(vseed)) {\n return isNegativeSide ? 'left' : 'right'\n }\n\n return isNegativeSide ? 'bottom' : 'top'\n}\n\nexport const inferDifferenceBracketDirection = (\n start: ResolvedDifferenceAnchor,\n end: ResolvedDifferenceAnchor,\n): 'left' | 'right' => {\n if (start.bandIndex === undefined || end.bandIndex === undefined) {\n return 'right'\n }\n\n return start.bandIndex <= end.bandIndex ? 'right' : 'left'\n}\n"],"names":["STACK_END_FIELD","toArray","value","Array","getDifferenceBandFields","spec","getDifferenceValueField","valueField","Error","normalizeDifferenceValue","selectorLabel","field","numberValue","Number","buildStackGroupDatum","datum","bandFields","Object","resolveBandIndex","dataset","bandDatum","bandIndex","isSubset","undefined","buildFallbackSelectorDatum","selectorValue","selectorList","fallbackDatum","currentSelector","isValueSelector","isFieldSelector","isMeasureSelector","operator","selectorValues","isDimensionSelector","isPartialDatumSelector","inferFallbackValue","numericCandidateKeys","key","resolveFallbackAnchor","options","inferredValue","coordinateDatum","hasMixedSigns","values","nonZeroValues","getDifferenceLineStackResolveMode","vseed","advancedVSeed","hasMultipleMeasure","hasMultipleMeasureInSingleView","usesDifferenceLineElementStackEnd","resolveDifferenceAnchor","stackResolveMode","allowSelectorFallback","matches","selector","stackGroups","Map","stackGroupDatum","JSON","groupRows","groupValues","sum","getStackRuntimeTotal","runtimeMatches","stackEndValues","Math","buildDifferenceCoordinateDatum","anchor","seriesData","relativeSeries","useElementStackEnd","runtimeMatch","runtimeStackEnd","runtimeStackTotal","getRuntimeDifferenceValue","buildDifferenceText","startValue","endValue","differenceType","inferDifferenceConnectDirection","isNegativeSide","isBarLikeChart","inferDifferenceBracketDirection","start","end"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"pipeline/spec/chart/pipes/annotation/annotationDifferenceLineCommon.js","sources":["../../../../../../../src/pipeline/spec/chart/pipes/annotation/annotationDifferenceLineCommon.ts"],"sourcesContent":["import type { IAreaChartSpec, IBarChartSpec, ICartesianSeries, ILineChartSpec } from '@visactor/vchart'\nimport {\n isDimensionSelector,\n isFieldSelector,\n isMeasureSelector,\n isPartialDatumSelector,\n isValueSelector,\n selector,\n} from '../../../../../dataSelector'\nimport { hasMultipleMeasureInSingleView, isBarLikeChart } from 'src/pipeline/utils'\nimport type { AdvancedVSeed, Datum, Selector, Selectors, VSeed } from 'src/types'\nimport { isSubset } from './utils'\n\ntype DifferenceSelectorLabel = 'start' | 'end'\ntype DifferenceChartSpec = IBarChartSpec | ILineChartSpec | IAreaChartSpec\nexport type DifferenceStackResolveMode = 'none' | 'stackTotal' | 'auto'\nexport type DifferenceAnchorMode = 'element' | 'stackTotal'\n\nexport type ResolvedDifferenceAnchor = {\n mode: DifferenceAnchorMode\n selectorLabel: DifferenceSelectorLabel\n coordinateDatum: Datum\n matchedDatum?: Datum\n stackGroupDatum?: Datum\n bandDatum: Datum\n bandIndex?: number\n value: number\n}\n\nconst STACK_END_FIELD = '__VCHART_STACK_END'\n\nconst toArray = <T>(value: T | T[] | undefined | null): T[] => {\n if (Array.isArray(value)) {\n return value\n }\n\n return value === undefined || value === null ? [] : [value]\n}\n\nconst getDifferenceBandFields = (spec: DifferenceChartSpec): string[] => {\n return toArray((spec.direction === 'horizontal' ? spec.yField : spec.xField) as string | string[] | undefined)\n}\n\nconst getDifferenceValueField = (spec: DifferenceChartSpec): string => {\n const valueField = toArray(\n (spec.direction === 'horizontal' ? spec.xField : spec.yField) as string | string[] | undefined,\n )[0]\n\n if (!valueField) {\n throw new Error('annotationDifferenceLine requires a value field in the target chart spec')\n }\n\n return valueField\n}\n\nconst normalizeDifferenceValue = (value: unknown, selectorLabel: DifferenceSelectorLabel, field: string): number => {\n const numberValue = Number(value)\n\n if (Number.isNaN(numberValue)) {\n throw new Error(\n `annotationDifferenceLine ${selectorLabel} selector resolved to a non-numeric value on field \"${field}\"`,\n )\n }\n\n return numberValue\n}\n\nconst buildStackGroupDatum = (datum: Datum, bandFields: string[]) => {\n return Object.fromEntries(bandFields.map((field) => [field, datum[field]])) as Datum\n}\n\nconst resolveBandIndex = (dataset: Datum[], bandDatum: Datum) => {\n const bandIndex = dataset.findIndex((datum) => isSubset(bandDatum, datum))\n\n return bandIndex >= 0 ? bandIndex : undefined\n}\n\nconst buildFallbackSelectorDatum = (selectorValue: Selector | Selectors): Datum | undefined => {\n const selectorList = Array.isArray(selectorValue) ? selectorValue : [selectorValue]\n const fallbackDatum: Datum = {}\n\n for (const currentSelector of selectorList) {\n if (isValueSelector(currentSelector) || isFieldSelector(currentSelector)) {\n return undefined\n }\n\n if (isMeasureSelector(currentSelector)) {\n const operator = currentSelector.operator || currentSelector.op\n const selectorValues = Array.isArray(currentSelector.value) ? currentSelector.value : [currentSelector.value]\n\n if (!['=', '=='].includes(operator as string) || selectorValues.length !== 1) {\n return undefined\n }\n\n fallbackDatum[currentSelector.field] = selectorValues[0]\n continue\n }\n\n if (isDimensionSelector(currentSelector)) {\n const operator = currentSelector.operator || currentSelector.op\n const selectorValues = Array.isArray(currentSelector.value) ? currentSelector.value : [currentSelector.value]\n\n if (operator !== 'in' || selectorValues.length !== 1) {\n return undefined\n }\n\n fallbackDatum[currentSelector.field] = selectorValues[0]\n continue\n }\n\n if (isPartialDatumSelector(currentSelector)) {\n Object.assign(fallbackDatum, currentSelector)\n continue\n }\n\n return undefined\n }\n\n return Object.keys(fallbackDatum).length > 0 ? fallbackDatum : undefined\n}\n\nconst inferFallbackValue = (fallbackDatum: Datum, valueField: string, bandFields: string[]) => {\n if (valueField in fallbackDatum) {\n return fallbackDatum[valueField]\n }\n\n const numericCandidateKeys = Object.keys(fallbackDatum).filter((key) => {\n if (key === valueField || bandFields.includes(key)) {\n return false\n }\n\n return typeof fallbackDatum[key] === 'number'\n })\n\n if (numericCandidateKeys.length !== 1) {\n return undefined\n }\n\n return fallbackDatum[numericCandidateKeys[0]]\n}\n\nconst resolveFallbackAnchor = (options: {\n dataset: Datum[]\n selectorLabel: DifferenceSelectorLabel\n selectorValue: Selector | Selectors\n valueField: string\n bandFields: string[]\n}): ResolvedDifferenceAnchor | undefined => {\n const { dataset, selectorLabel, selectorValue, valueField, bandFields } = options\n const fallbackDatum = buildFallbackSelectorDatum(selectorValue)\n\n if (!fallbackDatum) {\n return undefined\n }\n\n const inferredValue = inferFallbackValue(fallbackDatum, valueField, bandFields)\n\n if (inferredValue === undefined) {\n return undefined\n }\n\n const coordinateDatum = {\n ...fallbackDatum,\n [valueField]: inferredValue,\n }\n const bandDatum = buildStackGroupDatum(coordinateDatum, bandFields)\n\n try {\n return {\n mode: 'element',\n selectorLabel,\n coordinateDatum,\n matchedDatum: fallbackDatum,\n bandDatum,\n bandIndex: resolveBandIndex(dataset, bandDatum),\n value: normalizeDifferenceValue(inferredValue, selectorLabel, valueField),\n }\n } catch {\n return undefined\n }\n}\n\nconst hasMixedSigns = (values: number[]) => {\n const nonZeroValues = values.filter((value) => value !== 0)\n\n return nonZeroValues.some((value) => value > 0) && nonZeroValues.some((value) => value < 0)\n}\n\nexport const getDifferenceLineStackResolveMode = (\n vseed: VSeed,\n advancedVSeed: AdvancedVSeed,\n): DifferenceStackResolveMode => {\n const hasMultipleMeasure = hasMultipleMeasureInSingleView(advancedVSeed.reshapeMeasures ?? [])\n\n if (!hasMultipleMeasure) {\n return 'none'\n }\n\n if (vseed.chartType === 'column') {\n return 'auto'\n }\n\n if (vseed.chartType === 'bar') {\n return 'auto'\n }\n\n return 'none'\n}\n\nexport const usesDifferenceLineElementStackEnd = (vseed: VSeed, advancedVSeed: AdvancedVSeed) => {\n return vseed.chartType === 'area' && hasMultipleMeasureInSingleView(advancedVSeed.reshapeMeasures ?? [])\n}\n\nexport const resolveDifferenceAnchor = (options: {\n dataset: Datum[]\n selectorLabel: DifferenceSelectorLabel\n selectorValue: Selector | Selectors\n spec: DifferenceChartSpec\n stackResolveMode: DifferenceStackResolveMode\n allowSelectorFallback?: boolean\n}): ResolvedDifferenceAnchor | undefined => {\n const { dataset, selectorLabel, selectorValue, spec, stackResolveMode, allowSelectorFallback = true } = options\n const matches = dataset.filter((datum) => selector(datum, selectorValue))\n const valueField = getDifferenceValueField(spec)\n const bandFields = getDifferenceBandFields(spec)\n\n if (matches.length === 0) {\n return stackResolveMode === 'none' && allowSelectorFallback\n ? resolveFallbackAnchor({ dataset, selectorLabel, selectorValue, valueField, bandFields })\n : undefined\n }\n\n if (stackResolveMode === 'none' || (stackResolveMode === 'auto' && matches.length === 1)) {\n if (matches.length !== 1) {\n throw new Error(\n `annotationDifferenceLine ${selectorLabel} selector must resolve to exactly one datum, got ${matches.length}`,\n )\n }\n\n const bandDatum = buildStackGroupDatum(matches[0], bandFields)\n\n return {\n mode: 'element',\n selectorLabel,\n coordinateDatum: matches[0],\n matchedDatum: matches[0],\n bandDatum,\n bandIndex: resolveBandIndex(dataset, bandDatum),\n value: normalizeDifferenceValue(matches[0][valueField], selectorLabel, valueField),\n }\n }\n const stackGroups = new Map<string, Datum>()\n\n matches.forEach((datum) => {\n const stackGroupDatum = buildStackGroupDatum(datum, bandFields)\n stackGroups.set(JSON.stringify(stackGroupDatum), stackGroupDatum)\n })\n\n if (stackGroups.size !== 1) {\n throw new Error(\n `annotationDifferenceLine ${selectorLabel} selector must resolve to exactly one stack group, got ${stackGroups.size}`,\n )\n }\n\n const stackGroupDatum = Array.from(stackGroups.values())[0]\n const groupRows = dataset.filter((datum) => isSubset(stackGroupDatum, datum))\n\n if (stackResolveMode === 'auto' && groupRows.length !== matches.length) {\n throw new Error(\n `annotationDifferenceLine ${selectorLabel} selector must resolve to exactly one stack element or one full stack group`,\n )\n }\n const groupValues = groupRows.map((datum) => normalizeDifferenceValue(datum[valueField], selectorLabel, valueField))\n\n if (hasMixedSigns(groupValues)) {\n throw new Error('annotationDifferenceLine does not support mixed-sign stack totals in v1')\n }\n\n return {\n mode: 'stackTotal',\n selectorLabel,\n coordinateDatum: stackGroupDatum,\n matchedDatum: matches[0],\n stackGroupDatum,\n bandDatum: stackGroupDatum,\n bandIndex: resolveBandIndex(dataset, stackGroupDatum),\n value: groupValues.reduce((sum, value) => sum + value, 0),\n }\n}\n\nexport const getStackRuntimeTotal = (runtimeMatches: Datum[]) => {\n if (runtimeMatches.length === 0) {\n return undefined\n }\n\n const stackEndValues = runtimeMatches.map((datum) => Number(datum[STACK_END_FIELD]))\n\n if (stackEndValues.some((value) => !Number.isFinite(value))) {\n return undefined\n }\n\n if (hasMixedSigns(stackEndValues)) {\n throw new Error('annotationDifferenceLine does not support mixed-sign runtime stack totals in v1')\n }\n\n return stackEndValues.some((value) => value < 0) ? Math.min(...stackEndValues) : Math.max(...stackEndValues)\n}\n\nexport const buildDifferenceCoordinateDatum = (options: {\n anchor: ResolvedDifferenceAnchor\n seriesData: Datum[]\n relativeSeries: ICartesianSeries\n useElementStackEnd?: boolean\n}) => {\n const { anchor, seriesData, relativeSeries, useElementStackEnd = false } = options\n\n if (!anchor.stackGroupDatum) {\n const runtimeMatches = anchor.matchedDatum\n ? seriesData.filter((datum) => isSubset(anchor.matchedDatum as Datum, datum))\n : []\n\n if (!useElementStackEnd) {\n return runtimeMatches[0] ?? anchor.coordinateDatum\n }\n\n const runtimeMatch = runtimeMatches[0]\n const runtimeStackEnd = Number(runtimeMatch?.[STACK_END_FIELD])\n\n return {\n ...(runtimeMatch ?? anchor.coordinateDatum),\n [relativeSeries.getStackValueField()]: Number.isFinite(runtimeStackEnd) ? runtimeStackEnd : anchor.value,\n }\n }\n\n const runtimeMatches = seriesData.filter((datum) => isSubset(anchor.stackGroupDatum as Datum, datum))\n const runtimeStackTotal = getStackRuntimeTotal(runtimeMatches)\n\n return {\n ...(runtimeMatches[0] ?? anchor.coordinateDatum),\n [relativeSeries.getStackValueField()]: runtimeStackTotal ?? anchor.value,\n }\n}\n\nexport const getRuntimeDifferenceValue = (options: {\n anchor: ResolvedDifferenceAnchor\n seriesData: Datum[]\n useElementStackEnd?: boolean\n}) => {\n const { anchor, seriesData, useElementStackEnd = false } = options\n\n if (anchor.stackGroupDatum) {\n const runtimeMatches = seriesData.filter((datum) => isSubset(anchor.stackGroupDatum as Datum, datum))\n return getStackRuntimeTotal(runtimeMatches) ?? anchor.value\n }\n\n if (!useElementStackEnd || !anchor.matchedDatum) {\n return anchor.value\n }\n\n const runtimeMatch = seriesData.find((datum) => isSubset(anchor.matchedDatum as Datum, datum))\n const runtimeStackEnd = Number(runtimeMatch?.[STACK_END_FIELD])\n\n return Number.isFinite(runtimeStackEnd) ? runtimeStackEnd : anchor.value\n}\n\nexport const getDifferenceValue = (\n startValue: number,\n endValue: number,\n differenceType: 'absolute' | 'percent' = 'absolute',\n) => {\n if (differenceType === 'percent') {\n if (startValue === 0) {\n throw new Error('annotationDifferenceLine percent difference cannot be computed because start value is 0')\n }\n\n return (endValue - startValue) / startValue\n }\n\n return endValue - startValue\n}\n\nexport const buildDifferenceText = (\n startValue: number,\n endValue: number,\n differenceType: 'absolute' | 'percent' = 'absolute',\n formatter?: (value: number) => string,\n) => {\n const differenceValue = getDifferenceValue(startValue, endValue, differenceType)\n\n if (formatter) {\n return formatter(differenceValue)\n }\n\n if (differenceType === 'percent') {\n return `${(differenceValue * 100).toFixed(2)}%`\n }\n\n return `${differenceValue}`\n}\n\nexport const inferDifferenceConnectDirection = (vseed: VSeed, values: [number, number]) => {\n const isNegativeSide = values.every((value) => value < 0)\n\n if (isBarLikeChart(vseed)) {\n return isNegativeSide ? 'left' : 'right'\n }\n\n return isNegativeSide ? 'bottom' : 'top'\n}\n\nexport const inferDifferenceBracketDirection = (\n start: ResolvedDifferenceAnchor,\n end: ResolvedDifferenceAnchor,\n): 'left' | 'right' => {\n if (start.bandIndex === undefined || end.bandIndex === undefined) {\n return 'right'\n }\n\n return start.bandIndex <= end.bandIndex ? 'right' : 'left'\n}\n"],"names":["STACK_END_FIELD","toArray","value","Array","getDifferenceBandFields","spec","getDifferenceValueField","valueField","Error","normalizeDifferenceValue","selectorLabel","field","numberValue","Number","buildStackGroupDatum","datum","bandFields","Object","resolveBandIndex","dataset","bandDatum","bandIndex","isSubset","undefined","buildFallbackSelectorDatum","selectorValue","selectorList","fallbackDatum","currentSelector","isValueSelector","isFieldSelector","isMeasureSelector","operator","selectorValues","isDimensionSelector","isPartialDatumSelector","inferFallbackValue","numericCandidateKeys","key","resolveFallbackAnchor","options","inferredValue","coordinateDatum","hasMixedSigns","values","nonZeroValues","getDifferenceLineStackResolveMode","vseed","advancedVSeed","hasMultipleMeasure","hasMultipleMeasureInSingleView","usesDifferenceLineElementStackEnd","resolveDifferenceAnchor","stackResolveMode","allowSelectorFallback","matches","selector","stackGroups","Map","stackGroupDatum","JSON","groupRows","groupValues","sum","getStackRuntimeTotal","runtimeMatches","stackEndValues","Math","buildDifferenceCoordinateDatum","anchor","seriesData","relativeSeries","useElementStackEnd","runtimeMatch","runtimeStackEnd","runtimeStackTotal","getRuntimeDifferenceValue","getDifferenceValue","startValue","endValue","differenceType","buildDifferenceText","formatter","differenceValue","inferDifferenceConnectDirection","isNegativeSide","isBarLikeChart","inferDifferenceBracketDirection","start","end"],"mappings":";;;AA6BA,MAAMA,kBAAkB;AAExB,MAAMC,UAAU,CAAIC;IAClB,IAAIC,MAAM,OAAO,CAACD,QAChB,OAAOA;IAGT,OAAOA,QAAAA,QAAwC,EAAE,GAAG;QAACA;KAAM;AAC7D;AAEA,MAAME,0BAA0B,CAACC,OACxBJ,QAASI,AAAmB,iBAAnBA,KAAK,SAAS,GAAoBA,KAAK,MAAM,GAAGA,KAAK,MAAM;AAG7E,MAAMC,0BAA0B,CAACD;IAC/B,MAAME,aAAaN,QAChBI,AAAmB,iBAAnBA,KAAK,SAAS,GAAoBA,KAAK,MAAM,GAAGA,KAAK,MAAM,CAC7D,CAAC,EAAE;IAEJ,IAAI,CAACE,YACH,MAAM,IAAIC,MAAM;IAGlB,OAAOD;AACT;AAEA,MAAME,2BAA2B,CAACP,OAAgBQ,eAAwCC;IACxF,MAAMC,cAAcC,OAAOX;IAE3B,IAAIW,OAAO,KAAK,CAACD,cACf,MAAM,IAAIJ,MACR,CAAC,yBAAyB,EAAEE,cAAc,oDAAoD,EAAEC,MAAM,CAAC,CAAC;IAI5G,OAAOC;AACT;AAEA,MAAME,uBAAuB,CAACC,OAAcC,aACnCC,OAAO,WAAW,CAACD,WAAW,GAAG,CAAC,CAACL,QAAU;YAACA;YAAOI,KAAK,CAACJ,MAAM;SAAC;AAG3E,MAAMO,mBAAmB,CAACC,SAAkBC;IAC1C,MAAMC,YAAYF,QAAQ,SAAS,CAAC,CAACJ,QAAUO,SAASF,WAAWL;IAEnE,OAAOM,aAAa,IAAIA,YAAYE;AACtC;AAEA,MAAMC,6BAA6B,CAACC;IAClC,MAAMC,eAAevB,MAAM,OAAO,CAACsB,iBAAiBA,gBAAgB;QAACA;KAAc;IACnF,MAAME,gBAAuB,CAAC;IAE9B,KAAK,MAAMC,mBAAmBF,aAAc;QAC1C,IAAIG,gBAAgBD,oBAAoBE,gBAAgBF,kBACtD;QAGF,IAAIG,kBAAkBH,kBAAkB;YACtC,MAAMI,WAAWJ,gBAAgB,QAAQ,IAAIA,gBAAgB,EAAE;YAC/D,MAAMK,iBAAiB9B,MAAM,OAAO,CAACyB,gBAAgB,KAAK,IAAIA,gBAAgB,KAAK,GAAG;gBAACA,gBAAgB,KAAK;aAAC;YAE7G,IAAI,CAAC;gBAAC;gBAAK;aAAK,CAAC,QAAQ,CAACI,aAAuBC,AAA0B,MAA1BA,eAAe,MAAM,EACpE;YAGFN,aAAa,CAACC,gBAAgB,KAAK,CAAC,GAAGK,cAAc,CAAC,EAAE;YACxD;QACF;QAEA,IAAIC,oBAAoBN,kBAAkB;YACxC,MAAMI,WAAWJ,gBAAgB,QAAQ,IAAIA,gBAAgB,EAAE;YAC/D,MAAMK,iBAAiB9B,MAAM,OAAO,CAACyB,gBAAgB,KAAK,IAAIA,gBAAgB,KAAK,GAAG;gBAACA,gBAAgB,KAAK;aAAC;YAE7G,IAAII,AAAa,SAAbA,YAAqBC,AAA0B,MAA1BA,eAAe,MAAM,EAC5C;YAGFN,aAAa,CAACC,gBAAgB,KAAK,CAAC,GAAGK,cAAc,CAAC,EAAE;YACxD;QACF;QAEA,IAAIE,uBAAuBP,kBAAkB;YAC3CX,OAAO,MAAM,CAACU,eAAeC;YAC7B;QACF;QAEA;IACF;IAEA,OAAOX,OAAO,IAAI,CAACU,eAAe,MAAM,GAAG,IAAIA,gBAAgBJ;AACjE;AAEA,MAAMa,qBAAqB,CAACT,eAAsBpB,YAAoBS;IACpE,IAAIT,cAAcoB,eAChB,OAAOA,aAAa,CAACpB,WAAW;IAGlC,MAAM8B,uBAAuBpB,OAAO,IAAI,CAACU,eAAe,MAAM,CAAC,CAACW;QAC9D,IAAIA,QAAQ/B,cAAcS,WAAW,QAAQ,CAACsB,MAC5C,OAAO;QAGT,OAAO,AAA8B,YAA9B,OAAOX,aAAa,CAACW,IAAI;IAClC;IAEA,IAAID,AAAgC,MAAhCA,qBAAqB,MAAM,EAC7B;IAGF,OAAOV,aAAa,CAACU,oBAAoB,CAAC,EAAE,CAAC;AAC/C;AAEA,MAAME,wBAAwB,CAACC;IAO7B,MAAM,EAAErB,OAAO,EAAET,aAAa,EAAEe,aAAa,EAAElB,UAAU,EAAES,UAAU,EAAE,GAAGwB;IAC1E,MAAMb,gBAAgBH,2BAA2BC;IAEjD,IAAI,CAACE,eACH;IAGF,MAAMc,gBAAgBL,mBAAmBT,eAAepB,YAAYS;IAEpE,IAAIyB,AAAkBlB,WAAlBkB,eACF;IAGF,MAAMC,kBAAkB;QACtB,GAAGf,aAAa;QAChB,CAACpB,WAAW,EAAEkC;IAChB;IACA,MAAMrB,YAAYN,qBAAqB4B,iBAAiB1B;IAExD,IAAI;QACF,OAAO;YACL,MAAM;YACNN;YACAgC;YACA,cAAcf;YACdP;YACA,WAAWF,iBAAiBC,SAASC;YACrC,OAAOX,yBAAyBgC,eAAe/B,eAAeH;QAChE;IACF,EAAE,OAAM;QACN;IACF;AACF;AAEA,MAAMoC,gBAAgB,CAACC;IACrB,MAAMC,gBAAgBD,OAAO,MAAM,CAAC,CAAC1C,QAAUA,AAAU,MAAVA;IAE/C,OAAO2C,cAAc,IAAI,CAAC,CAAC3C,QAAUA,QAAQ,MAAM2C,cAAc,IAAI,CAAC,CAAC3C,QAAUA,QAAQ;AAC3F;AAEO,MAAM4C,oCAAoC,CAC/CC,OACAC;IAEA,MAAMC,qBAAqBC,+BAA+BF,cAAc,eAAe,IAAI,EAAE;IAE7F,IAAI,CAACC,oBACH,OAAO;IAGT,IAAIF,AAAoB,aAApBA,MAAM,SAAS,EACjB,OAAO;IAGT,IAAIA,AAAoB,UAApBA,MAAM,SAAS,EACjB,OAAO;IAGT,OAAO;AACT;AAEO,MAAMI,oCAAoC,CAACJ,OAAcC,gBACvDD,AAAoB,WAApBA,MAAM,SAAS,IAAeG,+BAA+BF,cAAc,eAAe,IAAI,EAAE;AAGlG,MAAMI,0BAA0B,CAACZ;IAQtC,MAAM,EAAErB,OAAO,EAAET,aAAa,EAAEe,aAAa,EAAEpB,IAAI,EAAEgD,gBAAgB,EAAEC,wBAAwB,IAAI,EAAE,GAAGd;IACxG,MAAMe,UAAUpC,QAAQ,MAAM,CAAC,CAACJ,QAAUyC,SAASzC,OAAOU;IAC1D,MAAMlB,aAAaD,wBAAwBD;IAC3C,MAAMW,aAAaZ,wBAAwBC;IAE3C,IAAIkD,AAAmB,MAAnBA,QAAQ,MAAM,EAChB,OAAOF,AAAqB,WAArBA,oBAA+BC,wBAClCf,sBAAsB;QAAEpB;QAAST;QAAee;QAAelB;QAAYS;IAAW,KACtFO;IAGN,IAAI8B,AAAqB,WAArBA,oBAAgCA,AAAqB,WAArBA,oBAA+BE,AAAmB,MAAnBA,QAAQ,MAAM,EAAS;QACxF,IAAIA,AAAmB,MAAnBA,QAAQ,MAAM,EAChB,MAAM,IAAI/C,MACR,CAAC,yBAAyB,EAAEE,cAAc,iDAAiD,EAAE6C,QAAQ,MAAM,EAAE;QAIjH,MAAMnC,YAAYN,qBAAqByC,OAAO,CAAC,EAAE,EAAEvC;QAEnD,OAAO;YACL,MAAM;YACNN;YACA,iBAAiB6C,OAAO,CAAC,EAAE;YAC3B,cAAcA,OAAO,CAAC,EAAE;YACxBnC;YACA,WAAWF,iBAAiBC,SAASC;YACrC,OAAOX,yBAAyB8C,OAAO,CAAC,EAAE,CAAChD,WAAW,EAAEG,eAAeH;QACzE;IACF;IACA,MAAMkD,cAAc,IAAIC;IAExBH,QAAQ,OAAO,CAAC,CAACxC;QACf,MAAM4C,kBAAkB7C,qBAAqBC,OAAOC;QACpDyC,YAAY,GAAG,CAACG,KAAK,SAAS,CAACD,kBAAkBA;IACnD;IAEA,IAAIF,AAAqB,MAArBA,YAAY,IAAI,EAClB,MAAM,IAAIjD,MACR,CAAC,yBAAyB,EAAEE,cAAc,uDAAuD,EAAE+C,YAAY,IAAI,EAAE;IAIzH,MAAME,kBAAkBxD,MAAM,IAAI,CAACsD,YAAY,MAAM,GAAG,CAAC,EAAE;IAC3D,MAAMI,YAAY1C,QAAQ,MAAM,CAAC,CAACJ,QAAUO,SAASqC,iBAAiB5C;IAEtE,IAAIsC,AAAqB,WAArBA,oBAA+BQ,UAAU,MAAM,KAAKN,QAAQ,MAAM,EACpE,MAAM,IAAI/C,MACR,CAAC,yBAAyB,EAAEE,cAAc,2EAA2E,CAAC;IAG1H,MAAMoD,cAAcD,UAAU,GAAG,CAAC,CAAC9C,QAAUN,yBAAyBM,KAAK,CAACR,WAAW,EAAEG,eAAeH;IAExG,IAAIoC,cAAcmB,cAChB,MAAM,IAAItD,MAAM;IAGlB,OAAO;QACL,MAAM;QACNE;QACA,iBAAiBiD;QACjB,cAAcJ,OAAO,CAAC,EAAE;QACxBI;QACA,WAAWA;QACX,WAAWzC,iBAAiBC,SAASwC;QACrC,OAAOG,YAAY,MAAM,CAAC,CAACC,KAAK7D,QAAU6D,MAAM7D,OAAO;IACzD;AACF;AAEO,MAAM8D,uBAAuB,CAACC;IACnC,IAAIA,AAA0B,MAA1BA,eAAe,MAAM,EACvB;IAGF,MAAMC,iBAAiBD,eAAe,GAAG,CAAC,CAAClD,QAAUF,OAAOE,KAAK,CAACf,gBAAgB;IAElF,IAAIkE,eAAe,IAAI,CAAC,CAAChE,QAAU,CAACW,OAAO,QAAQ,CAACX,SAClD;IAGF,IAAIyC,cAAcuB,iBAChB,MAAM,IAAI1D,MAAM;IAGlB,OAAO0D,eAAe,IAAI,CAAC,CAAChE,QAAUA,QAAQ,KAAKiE,KAAK,GAAG,IAAID,kBAAkBC,KAAK,GAAG,IAAID;AAC/F;AAEO,MAAME,iCAAiC,CAAC5B;IAM7C,MAAM,EAAE6B,MAAM,EAAEC,UAAU,EAAEC,cAAc,EAAEC,qBAAqB,KAAK,EAAE,GAAGhC;IAE3E,IAAI,CAAC6B,OAAO,eAAe,EAAE;QAC3B,MAAMJ,iBAAiBI,OAAO,YAAY,GACtCC,WAAW,MAAM,CAAC,CAACvD,QAAUO,SAAS+C,OAAO,YAAY,EAAWtD,UACpE,EAAE;QAEN,IAAI,CAACyD,oBACH,OAAOP,cAAc,CAAC,EAAE,IAAII,OAAO,eAAe;QAGpD,MAAMI,eAAeR,cAAc,CAAC,EAAE;QACtC,MAAMS,kBAAkB7D,OAAO4D,cAAc,CAACzE,gBAAgB;QAE9D,OAAO;YACL,GAAIyE,gBAAgBJ,OAAO,eAAe;YAC1C,CAACE,eAAe,kBAAkB,GAAG,EAAE1D,OAAO,QAAQ,CAAC6D,mBAAmBA,kBAAkBL,OAAO,KAAK;QAC1G;IACF;IAEA,MAAMJ,iBAAiBK,WAAW,MAAM,CAAC,CAACvD,QAAUO,SAAS+C,OAAO,eAAe,EAAWtD;IAC9F,MAAM4D,oBAAoBX,qBAAqBC;IAE/C,OAAO;QACL,GAAIA,cAAc,CAAC,EAAE,IAAII,OAAO,eAAe;QAC/C,CAACE,eAAe,kBAAkB,GAAG,EAAEI,qBAAqBN,OAAO,KAAK;IAC1E;AACF;AAEO,MAAMO,4BAA4B,CAACpC;IAKxC,MAAM,EAAE6B,MAAM,EAAEC,UAAU,EAAEE,qBAAqB,KAAK,EAAE,GAAGhC;IAE3D,IAAI6B,OAAO,eAAe,EAAE;QAC1B,MAAMJ,iBAAiBK,WAAW,MAAM,CAAC,CAACvD,QAAUO,SAAS+C,OAAO,eAAe,EAAWtD;QAC9F,OAAOiD,qBAAqBC,mBAAmBI,OAAO,KAAK;IAC7D;IAEA,IAAI,CAACG,sBAAsB,CAACH,OAAO,YAAY,EAC7C,OAAOA,OAAO,KAAK;IAGrB,MAAMI,eAAeH,WAAW,IAAI,CAAC,CAACvD,QAAUO,SAAS+C,OAAO,YAAY,EAAWtD;IACvF,MAAM2D,kBAAkB7D,OAAO4D,cAAc,CAACzE,gBAAgB;IAE9D,OAAOa,OAAO,QAAQ,CAAC6D,mBAAmBA,kBAAkBL,OAAO,KAAK;AAC1E;AAEO,MAAMQ,qBAAqB,CAChCC,YACAC,UACAC,iBAAyC,UAAU;IAEnD,IAAIA,AAAmB,cAAnBA,gBAA8B;QAChC,IAAIF,AAAe,MAAfA,YACF,MAAM,IAAItE,MAAM;QAGlB,OAAQuE,AAAAA,CAAAA,WAAWD,UAAS,IAAKA;IACnC;IAEA,OAAOC,WAAWD;AACpB;AAEO,MAAMG,sBAAsB,CACjCH,YACAC,UACAC,iBAAyC,UAAU,EACnDE;IAEA,MAAMC,kBAAkBN,mBAAmBC,YAAYC,UAAUC;IAEjE,IAAIE,WACF,OAAOA,UAAUC;IAGnB,IAAIH,AAAmB,cAAnBA,gBACF,OAAO,GAAIG,AAAAA,CAAAA,AAAkB,MAAlBA,eAAoB,EAAG,OAAO,CAAC,GAAG,CAAC,CAAC;IAGjD,OAAO,GAAGA,iBAAiB;AAC7B;AAEO,MAAMC,kCAAkC,CAACrC,OAAcH;IAC5D,MAAMyC,iBAAiBzC,OAAO,KAAK,CAAC,CAAC1C,QAAUA,QAAQ;IAEvD,IAAIoF,eAAevC,QACjB,OAAOsC,iBAAiB,SAAS;IAGnC,OAAOA,iBAAiB,WAAW;AACrC;AAEO,MAAME,kCAAkC,CAC7CC,OACAC;IAEA,IAAID,AAAoBjE,WAApBiE,MAAM,SAAS,IAAkBC,AAAkBlE,WAAlBkE,IAAI,SAAS,EAChD,OAAO;IAGT,OAAOD,MAAM,SAAS,IAAIC,IAAI,SAAS,GAAG,UAAU;AACtD"}
|
package/dist/umd/index.js
CHANGED
|
@@ -7251,8 +7251,9 @@ self.R = R;
|
|
|
7251
7251
|
};
|
|
7252
7252
|
};
|
|
7253
7253
|
const getStackRuntimeTotal = (runtimeMatches)=>{
|
|
7254
|
+
if (0 === runtimeMatches.length) return;
|
|
7254
7255
|
const stackEndValues = runtimeMatches.map((datum)=>Number(datum[STACK_END_FIELD]));
|
|
7255
|
-
if (stackEndValues.some((value)
|
|
7256
|
+
if (stackEndValues.some((value)=>!Number.isFinite(value))) return;
|
|
7256
7257
|
if (hasMixedSigns(stackEndValues)) throw new Error('annotationDifferenceLine does not support mixed-sign runtime stack totals in v1');
|
|
7257
7258
|
return stackEndValues.some((value)=>value < 0) ? Math.min(...stackEndValues) : Math.max(...stackEndValues);
|
|
7258
7259
|
};
|
|
@@ -7265,7 +7266,7 @@ self.R = R;
|
|
|
7265
7266
|
const runtimeStackEnd = Number(runtimeMatch?.[STACK_END_FIELD]);
|
|
7266
7267
|
return {
|
|
7267
7268
|
...runtimeMatch ?? anchor.coordinateDatum,
|
|
7268
|
-
[relativeSeries.getStackValueField()]: Number.
|
|
7269
|
+
[relativeSeries.getStackValueField()]: Number.isFinite(runtimeStackEnd) ? runtimeStackEnd : anchor.value
|
|
7269
7270
|
};
|
|
7270
7271
|
}
|
|
7271
7272
|
const runtimeMatches = seriesData.filter((datum)=>isSubset(anchor.stackGroupDatum, datum));
|
|
@@ -7284,14 +7285,20 @@ self.R = R;
|
|
|
7284
7285
|
if (!useElementStackEnd || !anchor.matchedDatum) return anchor.value;
|
|
7285
7286
|
const runtimeMatch = seriesData.find((datum)=>isSubset(anchor.matchedDatum, datum));
|
|
7286
7287
|
const runtimeStackEnd = Number(runtimeMatch?.[STACK_END_FIELD]);
|
|
7287
|
-
return Number.
|
|
7288
|
+
return Number.isFinite(runtimeStackEnd) ? runtimeStackEnd : anchor.value;
|
|
7288
7289
|
};
|
|
7289
|
-
const
|
|
7290
|
+
const getDifferenceValue = (startValue, endValue, differenceType = 'absolute')=>{
|
|
7290
7291
|
if ('percent' === differenceType) {
|
|
7291
7292
|
if (0 === startValue) throw new Error('annotationDifferenceLine percent difference cannot be computed because start value is 0');
|
|
7292
|
-
return
|
|
7293
|
+
return (endValue - startValue) / startValue;
|
|
7293
7294
|
}
|
|
7294
|
-
return
|
|
7295
|
+
return endValue - startValue;
|
|
7296
|
+
};
|
|
7297
|
+
const buildDifferenceText = (startValue, endValue, differenceType = 'absolute', formatter)=>{
|
|
7298
|
+
const differenceValue = getDifferenceValue(startValue, endValue, differenceType);
|
|
7299
|
+
if (formatter) return formatter(differenceValue);
|
|
7300
|
+
if ('percent' === differenceType) return `${(100 * differenceValue).toFixed(2)}%`;
|
|
7301
|
+
return `${differenceValue}`;
|
|
7295
7302
|
};
|
|
7296
7303
|
const inferDifferenceConnectDirection = (vseed, values)=>{
|
|
7297
7304
|
const isNegativeSide = values.every((value)=>value < 0);
|
|
@@ -7317,7 +7324,58 @@ self.R = R;
|
|
|
7317
7324
|
2,
|
|
7318
7325
|
2
|
|
7319
7326
|
];
|
|
7327
|
+
const DEFAULT_PERCENT_DIFFERENCE_FORMAT = {
|
|
7328
|
+
type: 'percent',
|
|
7329
|
+
fractionDigits: 2
|
|
7330
|
+
};
|
|
7320
7331
|
const getDifferenceLinePath = (index, total)=>1 === total ? 'annotationDifferenceLine' : `annotationDifferenceLine[${index}]`;
|
|
7332
|
+
const annotationDifferenceLine_toArray = (value)=>{
|
|
7333
|
+
if (Array.isArray(value)) return value;
|
|
7334
|
+
return null == value ? [] : [
|
|
7335
|
+
value
|
|
7336
|
+
];
|
|
7337
|
+
};
|
|
7338
|
+
const getAxisFormatter = (spec)=>{
|
|
7339
|
+
const valueAxisOrient = 'horizontal' === spec.direction ? 'bottom' : 'left';
|
|
7340
|
+
const formatMethod = spec.axes?.find((axis)=>axis.orient === valueAxisOrient)?.label?.formatMethod;
|
|
7341
|
+
return 'function' == typeof formatMethod ? (value)=>String(formatMethod(value) ?? value) : void 0;
|
|
7342
|
+
};
|
|
7343
|
+
const getExplicitMeasureFormat = (measure)=>{
|
|
7344
|
+
if (measure?.numFormat && !chunk_VCYTMP4D_n(measure.numFormat)) return measure.numFormat;
|
|
7345
|
+
if (measure?.format && !chunk_VCYTMP4D_n(measure.format)) return measure.format;
|
|
7346
|
+
};
|
|
7347
|
+
const inferMeasureIdFromDatum = (anchor, measureIds)=>{
|
|
7348
|
+
const candidateValues = [
|
|
7349
|
+
anchor.matchedDatum,
|
|
7350
|
+
anchor.coordinateDatum
|
|
7351
|
+
];
|
|
7352
|
+
for (const datum of candidateValues){
|
|
7353
|
+
if (!datum) continue;
|
|
7354
|
+
const candidates = measureIds.filter((measureId)=>Number(datum[measureId]) === anchor.value);
|
|
7355
|
+
if (1 === candidates.length) return candidates[0];
|
|
7356
|
+
}
|
|
7357
|
+
};
|
|
7358
|
+
const inferMeasureIdFromSelector = (selectorValue, measureIdSet)=>{
|
|
7359
|
+
const matchedMeasureIds = new Set();
|
|
7360
|
+
for (const currentSelector of annotationDifferenceLine_toArray(selectorValue)){
|
|
7361
|
+
if (isMeasureSelector(currentSelector)) {
|
|
7362
|
+
if (measureIdSet.has(currentSelector.field)) matchedMeasureIds.add(currentSelector.field);
|
|
7363
|
+
continue;
|
|
7364
|
+
}
|
|
7365
|
+
if (!(isFieldSelector(currentSelector) || isDimensionSelector(currentSelector))) {
|
|
7366
|
+
if (isPartialDatumSelector(currentSelector)) Object.keys(currentSelector).forEach((field)=>{
|
|
7367
|
+
if (measureIdSet.has(field)) matchedMeasureIds.add(field);
|
|
7368
|
+
});
|
|
7369
|
+
}
|
|
7370
|
+
}
|
|
7371
|
+
return 1 === matchedMeasureIds.size ? Array.from(matchedMeasureIds)[0] : void 0;
|
|
7372
|
+
};
|
|
7373
|
+
const resolveDifferenceMeasureId = (anchor, selectorValue, measureIds)=>{
|
|
7374
|
+
if ('element' !== anchor.mode) return;
|
|
7375
|
+
if (1 === measureIds.length) return measureIds[0];
|
|
7376
|
+
const measureIdSet = new Set(measureIds);
|
|
7377
|
+
return inferMeasureIdFromSelector(selectorValue, measureIdSet) ?? inferMeasureIdFromDatum(anchor, measureIds);
|
|
7378
|
+
};
|
|
7321
7379
|
const assertDifferenceLineConfig = (value, path)=>{
|
|
7322
7380
|
if ('object' != typeof value || null === value || Array.isArray(value)) throw new Error(`${path} must be an object`);
|
|
7323
7381
|
const start = value.start;
|
|
@@ -7340,6 +7398,10 @@ self.R = R;
|
|
|
7340
7398
|
const stackResolveMode = getDifferenceLineStackResolveMode(vseed, advancedVSeed);
|
|
7341
7399
|
const useElementStackEnd = usesDifferenceLineElementStackEnd(vseed, advancedVSeed);
|
|
7342
7400
|
const isBracketChart = 'line' === vseed.chartType || 'area' === vseed.chartType;
|
|
7401
|
+
const measures = findAllMeasures(advancedVSeed.measures);
|
|
7402
|
+
const measureIds = measures.map((measure)=>measure.id);
|
|
7403
|
+
const axisFormatter = getAxisFormatter(chartSpec);
|
|
7404
|
+
const percentFormatter = createFormatter(DEFAULT_PERCENT_DIFFERENCE_FORMAT);
|
|
7343
7405
|
const markLine = annotationDifferenceLineList.flatMap((annotationDifferenceLine, index)=>{
|
|
7344
7406
|
try {
|
|
7345
7407
|
assertDifferenceLineConfig(annotationDifferenceLine, getDifferenceLinePath(index, annotationDifferenceLineList.length));
|
|
@@ -7373,6 +7435,11 @@ self.R = R;
|
|
|
7373
7435
|
const textBackgroundColor = annotationDifferenceLine.textBackgroundColor ?? theme?.textBackgroundColor ?? DEFAULT_TEXT_BACKGROUND_COLOR;
|
|
7374
7436
|
const textFontSize = annotationDifferenceLine.textFontSize ?? theme?.textFontSize ?? DEFAULT_TEXT_FONT_SIZE;
|
|
7375
7437
|
const differenceType = annotationDifferenceLine.differenceType ?? 'absolute';
|
|
7438
|
+
const startMeasureId = resolveDifferenceMeasureId(start, annotationDifferenceLine.start.selector, measureIds);
|
|
7439
|
+
const endMeasureId = resolveDifferenceMeasureId(end, annotationDifferenceLine.end.selector, measureIds);
|
|
7440
|
+
const sameMeasure = void 0 !== startMeasureId && startMeasureId === endMeasureId;
|
|
7441
|
+
const explicitMeasureFormat = sameMeasure ? getExplicitMeasureFormat(measures.find((measure)=>measure.id === startMeasureId)) : void 0;
|
|
7442
|
+
const differenceFormatter = 'percent' === differenceType ? percentFormatter : explicitMeasureFormat ? createFormatter(explicitMeasureFormat) : axisFormatter ?? autoFormatter;
|
|
7376
7443
|
const label = usesRuntimeStackEnd ? {
|
|
7377
7444
|
visible: true,
|
|
7378
7445
|
position: 'middle',
|
|
@@ -7387,7 +7454,7 @@ self.R = R;
|
|
|
7387
7454
|
anchor: end,
|
|
7388
7455
|
seriesData,
|
|
7389
7456
|
useElementStackEnd: usesRuntimeStackEnd
|
|
7390
|
-
}), differenceType);
|
|
7457
|
+
}), differenceType, differenceFormatter);
|
|
7391
7458
|
} catch {
|
|
7392
7459
|
return '';
|
|
7393
7460
|
}
|
|
@@ -7411,7 +7478,7 @@ self.R = R;
|
|
|
7411
7478
|
visible: true,
|
|
7412
7479
|
position: 'middle',
|
|
7413
7480
|
refY: 0,
|
|
7414
|
-
text: buildDifferenceText(start.value, end.value, differenceType),
|
|
7481
|
+
text: buildDifferenceText(start.value, end.value, differenceType, differenceFormatter),
|
|
7415
7482
|
style: {
|
|
7416
7483
|
fill: textColor,
|
|
7417
7484
|
fontSize: textFontSize
|