1- import { BasicSeriesSpec , DomainRange } from '../utils/specs' ;
2- import { GroupId , SpecId } from '../../../utils/ids' ;
1+ import { BasicSeriesSpec , DomainRange , DEFAULT_GLOBAL_ID } from '../utils/specs' ;
2+ import { GroupId , SpecId , getGroupId } from '../../../utils/ids' ;
33import { ScaleContinuousType , ScaleType } from '../../../utils/scales/scales' ;
44import { isCompleteBound , isLowerBound , isUpperBound } from '../utils/axis_utils' ;
55import { BaseDomain } from './domain' ;
@@ -16,81 +16,121 @@ export type YDomain = BaseDomain & {
1616} ;
1717export type YBasicSeriesSpec = Pick <
1818 BasicSeriesSpec ,
19- 'id' | 'seriesType' | 'yScaleType' | 'groupId' | 'stackAccessors' | 'yScaleToDataExtent' | 'styleAccessor'
19+ | 'id'
20+ | 'seriesType'
21+ | 'yScaleType'
22+ | 'groupId'
23+ | 'stackAccessors'
24+ | 'yScaleToDataExtent'
25+ | 'styleAccessor'
26+ | 'useDefaultGroupDomain'
2027> & { stackAsPercentage ?: boolean } ;
2128
29+ interface GroupSpecs {
30+ isPercentageStack : boolean ;
31+ stacked : YBasicSeriesSpec [ ] ;
32+ nonStacked : YBasicSeriesSpec [ ] ;
33+ }
34+
2235export function mergeYDomain (
2336 dataSeries : Map < SpecId , RawDataSeries [ ] > ,
2437 specs : YBasicSeriesSpec [ ] ,
2538 domainsByGroupId : Map < GroupId , DomainRange > ,
2639) : YDomain [ ] {
2740 // group specs by group ids
2841 const specsByGroupIds = splitSpecsByGroupId ( specs ) ;
29-
3042 const specsByGroupIdsEntries = [ ...specsByGroupIds . entries ( ) ] ;
43+ const globalId = getGroupId ( DEFAULT_GLOBAL_ID ) ;
3144
32- const yDomains = specsByGroupIdsEntries . map (
33- ( [ groupId , groupSpecs ] ) : YDomain => {
34- const groupYScaleType = coerceYScaleTypes ( [ ...groupSpecs . stacked , ...groupSpecs . nonStacked ] ) ;
35- const { isPercentageStack } = groupSpecs ;
36-
37- let domain : number [ ] ;
38- if ( isPercentageStack ) {
39- domain = computeContinuousDataDomain ( [ 0 , 1 ] , identity ) ;
40- } else {
41- // compute stacked domain
42- const isStackedScaleToExtent = groupSpecs . stacked . some ( ( spec ) => {
43- return spec . yScaleToDataExtent ;
44- } ) ;
45- const stackedDataSeries = getDataSeriesOnGroup ( dataSeries , groupSpecs . stacked ) ;
46- const stackedDomain = computeYStackedDomain ( stackedDataSeries , isStackedScaleToExtent ) ;
47-
48- // compute non stacked domain
49- const isNonStackedScaleToExtent = groupSpecs . nonStacked . some ( ( spec ) => {
50- return spec . yScaleToDataExtent ;
51- } ) ;
52- const nonStackedDataSeries = getDataSeriesOnGroup ( dataSeries , groupSpecs . nonStacked ) ;
53- const nonStackedDomain = computeYNonStackedDomain ( nonStackedDataSeries , isNonStackedScaleToExtent ) ;
54-
55- // merge stacked and non stacked domain together
56- domain = computeContinuousDataDomain (
57- [ ...stackedDomain , ...nonStackedDomain ] ,
58- identity ,
59- isStackedScaleToExtent || isNonStackedScaleToExtent ,
60- ) ;
61-
62- const [ computedDomainMin , computedDomainMax ] = domain ;
63-
64- const customDomain = domainsByGroupId . get ( groupId ) ;
65-
66- if ( customDomain && isCompleteBound ( customDomain ) ) {
67- // Don't need to check min > max because this has been validated on axis domain merge
68- domain = [ customDomain . min , customDomain . max ] ;
69- } else if ( customDomain && isLowerBound ( customDomain ) ) {
70- if ( customDomain . min > computedDomainMax ) {
71- throw new Error ( `custom yDomain for ${ groupId } is invalid, custom min is greater than computed max` ) ;
72- }
73-
74- domain = [ customDomain . min , computedDomainMax ] ;
75- } else if ( customDomain && isUpperBound ( customDomain ) ) {
76- if ( computedDomainMin > customDomain . max ) {
77- throw new Error ( `custom yDomain for ${ groupId } is invalid, computed min is greater than custom max` ) ;
78- }
79-
80- domain = [ computedDomainMin , customDomain . max ] ;
81- }
82- }
45+ const yDomains = specsByGroupIdsEntries . map < YDomain > ( ( [ groupId , groupSpecs ] ) => {
46+ const customDomain = domainsByGroupId . get ( groupId ) ;
47+ return mergeYDomainForGroup ( dataSeries , groupId , groupSpecs , customDomain ) ;
48+ } ) ;
49+
50+ const globalGroupIds : Set < GroupId > = specs . reduce < Set < GroupId > > ( ( acc , { groupId, useDefaultGroupDomain } ) => {
51+ if ( groupId !== globalId && useDefaultGroupDomain ) {
52+ acc . add ( groupId ) ;
53+ }
54+ return acc ;
55+ } , new Set ( ) ) ;
56+ globalGroupIds . add ( globalId ) ;
57+
58+ const globalYDomains = yDomains . filter ( ( domain ) => globalGroupIds . has ( domain . groupId ) ) ;
59+ let globalYDomain = [ Number . MAX_SAFE_INTEGER , Number . MIN_SAFE_INTEGER ] ;
60+ globalYDomains . forEach ( ( domain ) => {
61+ globalYDomain = [ Math . min ( globalYDomain [ 0 ] , domain . domain [ 0 ] ) , Math . max ( globalYDomain [ 1 ] , domain . domain [ 1 ] ) ] ;
62+ } ) ;
63+ return yDomains . map ( ( domain ) => {
64+ if ( globalGroupIds . has ( domain . groupId ) ) {
8365 return {
84- type : 'yDomain' ,
85- isBandScale : false ,
86- scaleType : groupYScaleType ,
87- groupId,
88- domain,
66+ ...domain ,
67+ domain : globalYDomain ,
8968 } ;
90- } ,
91- ) ;
69+ }
70+ return domain ;
71+ } ) ;
72+ }
73+
74+ function mergeYDomainForGroup (
75+ dataSeries : Map < SpecId , RawDataSeries [ ] > ,
76+ groupId : GroupId ,
77+ groupSpecs : GroupSpecs ,
78+ customDomain ?: DomainRange ,
79+ ) : YDomain {
80+ const groupYScaleType = coerceYScaleTypes ( [ ...groupSpecs . stacked , ...groupSpecs . nonStacked ] ) ;
81+ const { isPercentageStack } = groupSpecs ;
82+
83+ let domain : number [ ] ;
84+ if ( isPercentageStack ) {
85+ domain = computeContinuousDataDomain ( [ 0 , 1 ] , identity ) ;
86+ } else {
87+ // compute stacked domain
88+ const isStackedScaleToExtent = groupSpecs . stacked . some ( ( spec ) => {
89+ return spec . yScaleToDataExtent ;
90+ } ) ;
91+ const stackedDataSeries = getDataSeriesOnGroup ( dataSeries , groupSpecs . stacked ) ;
92+ const stackedDomain = computeYStackedDomain ( stackedDataSeries , isStackedScaleToExtent ) ;
9293
93- return yDomains ;
94+ // compute non stacked domain
95+ const isNonStackedScaleToExtent = groupSpecs . nonStacked . some ( ( spec ) => {
96+ return spec . yScaleToDataExtent ;
97+ } ) ;
98+ const nonStackedDataSeries = getDataSeriesOnGroup ( dataSeries , groupSpecs . nonStacked ) ;
99+ const nonStackedDomain = computeYNonStackedDomain ( nonStackedDataSeries , isNonStackedScaleToExtent ) ;
100+
101+ // merge stacked and non stacked domain together
102+ domain = computeContinuousDataDomain (
103+ [ ...stackedDomain , ...nonStackedDomain ] ,
104+ identity ,
105+ isStackedScaleToExtent || isNonStackedScaleToExtent ,
106+ ) ;
107+
108+ const [ computedDomainMin , computedDomainMax ] = domain ;
109+
110+ if ( customDomain && isCompleteBound ( customDomain ) ) {
111+ // Don't need to check min > max because this has been validated on axis domain merge
112+ domain = [ customDomain . min , customDomain . max ] ;
113+ } else if ( customDomain && isLowerBound ( customDomain ) ) {
114+ if ( customDomain . min > computedDomainMax ) {
115+ throw new Error ( `custom yDomain for ${ groupId } is invalid, custom min is greater than computed max` ) ;
116+ }
117+
118+ domain = [ customDomain . min , computedDomainMax ] ;
119+ } else if ( customDomain && isUpperBound ( customDomain ) ) {
120+ if ( computedDomainMin > customDomain . max ) {
121+ throw new Error ( `custom yDomain for ${ groupId } is invalid, computed min is greater than custom max` ) ;
122+ }
123+
124+ domain = [ computedDomainMin , customDomain . max ] ;
125+ }
126+ }
127+ return {
128+ type : 'yDomain' ,
129+ isBandScale : false ,
130+ scaleType : groupYScaleType ,
131+ groupId : groupId ,
132+ domain,
133+ } ;
94134}
95135
96136export function getDataSeriesOnGroup (
0 commit comments