@orbcharts/plugins-basic 3.0.0-alpha.56 → 3.0.0-alpha.57

Sign up to get free protection for your applications and to get access to all the features.
Files changed (87) hide show
  1. package/LICENSE +200 -200
  2. package/dist/orbcharts-plugins-basic.es.js +7064 -7067
  3. package/dist/orbcharts-plugins-basic.umd.js +10 -10
  4. package/dist/src/base/BaseLineAreas.d.ts +1 -0
  5. package/dist/src/base/BaseLines.d.ts +3 -1
  6. package/dist/src/grid/gridObservables.d.ts +19 -2
  7. package/dist/src/grid/types.d.ts +1 -0
  8. package/dist/vite.config.d.mts +2 -0
  9. package/package.json +43 -42
  10. package/src/base/BaseBarStack.ts +778 -778
  11. package/src/base/BaseBars.ts +764 -764
  12. package/src/base/BaseBarsTriangle.ts +672 -672
  13. package/src/base/BaseDots.ts +513 -513
  14. package/src/base/BaseGroupAxis.ts +558 -558
  15. package/src/base/BaseLegend.ts +641 -641
  16. package/src/base/BaseLineAreas.ts +628 -625
  17. package/src/base/BaseLines.ts +704 -699
  18. package/src/base/BaseValueAxis.ts +478 -478
  19. package/src/base/types.ts +2 -2
  20. package/src/grid/defaults.ts +128 -127
  21. package/src/grid/gridObservables.ts +539 -248
  22. package/src/grid/index.ts +15 -15
  23. package/src/grid/plugins/BarStack.ts +43 -43
  24. package/src/grid/plugins/Bars.ts +44 -44
  25. package/src/grid/plugins/BarsPN.ts +41 -41
  26. package/src/grid/plugins/BarsTriangle.ts +42 -42
  27. package/src/grid/plugins/Dots.ts +37 -37
  28. package/src/grid/plugins/GridLegend.ts +59 -59
  29. package/src/grid/plugins/GroupAux.ts +976 -922
  30. package/src/grid/plugins/GroupAxis.ts +35 -35
  31. package/src/grid/plugins/LineAreas.ts +40 -39
  32. package/src/grid/plugins/Lines.ts +40 -38
  33. package/src/grid/plugins/ScalingArea.ts +173 -173
  34. package/src/grid/plugins/ValueAxis.ts +36 -36
  35. package/src/grid/plugins/ValueStackAxis.ts +38 -38
  36. package/src/grid/types.ts +123 -122
  37. package/src/index.ts +9 -9
  38. package/src/multiGrid/defaults.ts +158 -158
  39. package/src/multiGrid/index.ts +13 -13
  40. package/src/multiGrid/multiGridObservables.ts +49 -49
  41. package/src/multiGrid/plugins/MultiBarStack.ts +78 -78
  42. package/src/multiGrid/plugins/MultiBars.ts +77 -77
  43. package/src/multiGrid/plugins/MultiBarsTriangle.ts +77 -77
  44. package/src/multiGrid/plugins/MultiDots.ts +65 -65
  45. package/src/multiGrid/plugins/MultiGridLegend.ts +89 -89
  46. package/src/multiGrid/plugins/MultiGroupAxis.ts +69 -69
  47. package/src/multiGrid/plugins/MultiLineAreas.ts +77 -67
  48. package/src/multiGrid/plugins/MultiLines.ts +77 -66
  49. package/src/multiGrid/plugins/MultiValueAxis.ts +69 -69
  50. package/src/multiGrid/plugins/MultiValueStackAxis.ts +69 -69
  51. package/src/multiGrid/plugins/OverlappingValueAxes.ts +167 -167
  52. package/src/multiGrid/plugins/OverlappingValueStackAxes.ts +168 -168
  53. package/src/multiGrid/types.ts +72 -72
  54. package/src/noneData/defaults.ts +102 -102
  55. package/src/noneData/index.ts +3 -3
  56. package/src/noneData/plugins/Container.ts +10 -10
  57. package/src/noneData/plugins/Tooltip.ts +310 -310
  58. package/src/noneData/types.ts +26 -26
  59. package/src/series/defaults.ts +144 -144
  60. package/src/series/index.ts +9 -9
  61. package/src/series/plugins/Bubbles.ts +545 -545
  62. package/src/series/plugins/Pie.ts +576 -576
  63. package/src/series/plugins/PieEventTexts.ts +262 -262
  64. package/src/series/plugins/PieLabels.ts +304 -304
  65. package/src/series/plugins/Rose.ts +472 -472
  66. package/src/series/plugins/RoseLabels.ts +362 -362
  67. package/src/series/plugins/SeriesLegend.ts +59 -59
  68. package/src/series/seriesObservables.ts +145 -145
  69. package/src/series/seriesUtils.ts +51 -51
  70. package/src/series/types.ts +83 -83
  71. package/src/tree/defaults.ts +23 -23
  72. package/src/tree/index.ts +3 -3
  73. package/src/tree/plugins/TreeLegend.ts +59 -59
  74. package/src/tree/plugins/TreeMap.ts +305 -305
  75. package/src/tree/types.ts +23 -23
  76. package/src/utils/commonUtils.ts +21 -21
  77. package/src/utils/d3Graphics.ts +124 -124
  78. package/src/utils/d3Utils.ts +73 -73
  79. package/src/utils/observables.ts +14 -14
  80. package/src/utils/orbchartsUtils.ts +100 -100
  81. package/{tsconfig.json → tsconfig.base.json} +13 -13
  82. package/tsconfig.json.bak.vite-plugin-tsconfig +8 -0
  83. package/tsconfig.prod.json +2 -13
  84. package/vite.config.mjs +41 -0
  85. package/dist/vite.config.d.ts +0 -2
  86. package/tsconfig.dev.json +0 -17
  87. package/vite.config.js +0 -50
@@ -1,248 +1,539 @@
1
- import * as d3 from 'd3'
2
- import {
3
- Observable,
4
- Subject,
5
- of,
6
- takeUntil,
7
- filter,
8
- map,
9
- switchMap,
10
- combineLatest,
11
- merge,
12
- shareReplay,
13
- distinctUntilChanged
14
- } from 'rxjs'
15
- import type {
16
- ChartParams,
17
- HighlightTarget,
18
- DataFormatterGrid,
19
- ComputedDataGrid,
20
- ComputedDatumGrid,
21
- TransformData,
22
- GridContainerPosition,
23
- Layout } from '@orbcharts/core'
24
- import { createAxisQuantizeScale } from '@orbcharts/core'
25
- import { getClassName, getUniID } from '../utils/orbchartsUtils'
26
-
27
- // grid選取器
28
- export const gridSelectionsObservable = ({ selection, pluginName, clipPathID, seriesLabels$, gridContainerPosition$, gridAxesTransform$, gridGraphicTransform$ }: {
29
- selection: d3.Selection<any, unknown, any, unknown>
30
- pluginName: string
31
- clipPathID: string
32
- // computedData$: Observable<ComputedDataGrid>
33
- seriesLabels$: Observable<string[]>
34
- gridContainerPosition$: Observable<GridContainerPosition[]>
35
- gridAxesTransform$: Observable<TransformData>
36
- gridGraphicTransform$: Observable<TransformData>
37
- }) => {
38
- const seriesClassName = getClassName(pluginName, 'series')
39
- const axesClassName = getClassName(pluginName, 'axes')
40
- const graphicClassName = getClassName(pluginName, 'graphic')
41
-
42
- const seriesSelection$ = seriesLabels$.pipe(
43
- map((existSeriesLabels, i) => {
44
- return selection
45
- .selectAll<SVGGElement, string>(`g.${seriesClassName}`)
46
- .data(existSeriesLabels, d => d)
47
- .join(
48
- enter => {
49
- return enter
50
- .append('g')
51
- .classed(seriesClassName, true)
52
- .each((d, i, g) => {
53
- const axesSelection = d3.select(g[i])
54
- .selectAll<SVGGElement, ComputedDatumGrid[]>(`g.${axesClassName}`)
55
- .data([i])
56
- .join(
57
- enter => {
58
- return enter
59
- .append('g')
60
- .classed(axesClassName, true)
61
- .attr('clip-path', `url(#${clipPathID})`)
62
- .each((d, i, g) => {
63
- const defsSelection = d3.select(g[i])
64
- .selectAll<SVGDefsElement, any>('defs')
65
- .data([i])
66
- .join('defs')
67
-
68
- const graphicGSelection = d3.select(g[i])
69
- .selectAll<SVGGElement, any>('g')
70
- .data([i])
71
- .join('g')
72
- .classed(graphicClassName, true)
73
- })
74
- },
75
- update => update,
76
- exit => exit.remove()
77
- )
78
- })
79
- },
80
- update => update,
81
- exit => exit.remove()
82
- )
83
- }),
84
- shareReplay(1)
85
- )
86
-
87
- combineLatest({
88
- seriesSelection: seriesSelection$,
89
- gridContainerPosition: gridContainerPosition$
90
- }).pipe(
91
- switchMap(async d => d)
92
- ).subscribe(data => {
93
- data.seriesSelection
94
- .transition()
95
- .attr('transform', (d, i) => {
96
- const gridContainerPosition = data.gridContainerPosition[i] ?? data.gridContainerPosition[0]
97
- const translate = gridContainerPosition.translate
98
- const scale = gridContainerPosition.scale
99
- return `translate(${translate[0]}, ${translate[1]}) scale(${scale[0]}, ${scale[1]})`
100
- })
101
- })
102
-
103
- const axesSelection$ = combineLatest({
104
- seriesSelection: seriesSelection$,
105
- gridAxesTransform: gridAxesTransform$
106
- }).pipe(
107
- switchMap(async d => d),
108
- map(data => {
109
- return data.seriesSelection
110
- .select<SVGGElement>(`g.${axesClassName}`)
111
- .style('transform', data.gridAxesTransform.value)
112
- }),
113
- shareReplay(1)
114
- )
115
- const defsSelection$ = axesSelection$.pipe(
116
- map(axesSelection => {
117
- return axesSelection.select<SVGDefsElement>('defs')
118
- }),
119
- shareReplay(1)
120
- )
121
- const graphicGSelection$ = combineLatest({
122
- axesSelection: axesSelection$,
123
- gridGraphicTransform: gridGraphicTransform$
124
- }).pipe(
125
- switchMap(async d => d),
126
- map(data => {
127
- const graphicGSelection = data.axesSelection
128
- .select<SVGGElement>(`g.${graphicClassName}`)
129
- graphicGSelection
130
- .transition()
131
- .duration(50)
132
- .style('transform', data.gridGraphicTransform.value)
133
- return graphicGSelection
134
- }),
135
- shareReplay(1)
136
- )
137
-
138
- return {
139
- seriesSelection$,
140
- axesSelection$,
141
- defsSelection$,
142
- graphicGSelection$
143
- }
144
- }
145
-
146
- // 由事件取得group data的function
147
- export const gridGroupPositionFnObservable = ({ fullDataFormatter$, gridAxesSize$, computedData$, fullChartParams$ }: {
148
- fullDataFormatter$: Observable<DataFormatterGrid>
149
- gridAxesSize$: Observable<{
150
- width: number;
151
- height: number;
152
- }>
153
- computedData$: Observable<ComputedDataGrid>
154
- // GroupDataMap$: Observable<Map<string, ComputedDatumGrid[]>>
155
- fullChartParams$: Observable<ChartParams>
156
- }): Observable<(event: any) => { groupIndex: number; groupLabel: string }> => {
157
- const destroy$ = new Subject()
158
-
159
- // 顯示範圍內的group labels
160
- const scaleRangeGroupLabels$: Observable<string[]> = new Observable(subscriber => {
161
- combineLatest({
162
- dataFormatter: fullDataFormatter$,
163
- computedData: computedData$
164
- }).pipe(
165
- takeUntil(destroy$),
166
- switchMap(async (d) => d),
167
- ).subscribe(data => {
168
- const groupMin = 0
169
- const groupMax = data.computedData[0] ? data.computedData[0].length - 1 : 0
170
- const groupScaleDomainMin = data.dataFormatter.grid.groupAxis.scaleDomain[0] === 'auto'
171
- ? groupMin - data.dataFormatter.grid.groupAxis.scalePadding
172
- : data.dataFormatter.grid.groupAxis.scaleDomain[0] as number - data.dataFormatter.grid.groupAxis.scalePadding
173
- const groupScaleDomainMax = data.dataFormatter.grid.groupAxis.scaleDomain[1] === 'auto'
174
- ? groupMax + data.dataFormatter.grid.groupAxis.scalePadding
175
- : data.dataFormatter.grid.groupAxis.scaleDomain[1] as number + data.dataFormatter.grid.groupAxis.scalePadding
176
-
177
- // const groupingAmount = data.computedData[0]
178
- // ? data.computedData[0].length
179
- // : 0
180
-
181
- let _labels = data.dataFormatter.grid.seriesDirection === 'row'
182
- ? (data.computedData[0] ?? []).map(d => d.groupLabel)
183
- : data.computedData.map(d => d[0].groupLabel)
184
-
185
- const _axisLabels =
186
- // new Array(groupingAmount).fill(0)
187
- // .map((d, i) => {
188
- // return _labels[i] != null
189
- // ? _labels[i]
190
- // : String(i) // 沒有label則用序列號填充
191
- // })
192
- _labels
193
- .filter((d, i) => {
194
- return i >= groupScaleDomainMin && i <= groupScaleDomainMax
195
- })
196
- subscriber.next(_axisLabels)
197
- })
198
- })
199
-
200
- return new Observable<(event: any) => { groupIndex: number; groupLabel: string }>(subscriber => {
201
- combineLatest({
202
- dataFormatter: fullDataFormatter$,
203
- axisSize: gridAxesSize$,
204
- fullChartParams: fullChartParams$,
205
- scaleRangeGroupLabels: scaleRangeGroupLabels$
206
- }).pipe(
207
- takeUntil(destroy$),
208
- switchMap(async (d) => d),
209
- ).subscribe(data => {
210
-
211
- const reverse = data.dataFormatter.grid.valueAxis.position === 'right'
212
- || data.dataFormatter.grid.valueAxis.position === 'bottom'
213
- ? true : false
214
-
215
- // 比例尺座標對應非連續資料索引
216
- const groupIndexScale = createAxisQuantizeScale({
217
- axisLabels: data.scaleRangeGroupLabels,
218
- axisWidth: data.axisSize.width,
219
- reverse
220
- })
221
-
222
- // 依比例尺位置計算座標
223
- const axisValuePredicate = (event: any) => {
224
- return data.dataFormatter.grid.groupAxis.position === 'bottom'
225
- || data.dataFormatter.grid.groupAxis.position === 'top'
226
- ? event.offsetX - data.fullChartParams.padding.left
227
- : event.offsetY - data.fullChartParams.padding.top
228
- }
229
-
230
- // 比例尺座標取得groupData的function
231
- const createEventGroupData: (event: any) => { groupIndex: number; groupLabel: string } = (event: any) => {
232
- const axisValue = axisValuePredicate(event)
233
- const groupIndex = groupIndexScale(axisValue)
234
- return {
235
- groupIndex,
236
- groupLabel: data.scaleRangeGroupLabels[groupIndex] ?? ''
237
- }
238
- }
239
-
240
- subscriber.next(createEventGroupData)
241
-
242
- return function unsubscribe () {
243
- destroy$.next(undefined)
244
- }
245
- })
246
- })
247
- }
248
-
1
+ import * as d3 from 'd3'
2
+ import {
3
+ Observable,
4
+ Subject,
5
+ of,
6
+ takeUntil,
7
+ filter,
8
+ map,
9
+ switchMap,
10
+ combineLatest,
11
+ merge,
12
+ shareReplay,
13
+ distinctUntilChanged
14
+ } from 'rxjs'
15
+ import type {
16
+ ChartParams,
17
+ HighlightTarget,
18
+ DataFormatterGrid,
19
+ ComputedDataGrid,
20
+ ComputedDatumGrid,
21
+ TransformData,
22
+ GridContainerPosition,
23
+ Layout } from '@orbcharts/core'
24
+ import { createAxisQuantizeScale } from '@orbcharts/core'
25
+ import { getClassName, getUniID } from '../utils/orbchartsUtils'
26
+ import { d3EventObservable } from '../utils/observables'
27
+
28
+ // grid選取器
29
+ export const gridSelectionsObservable = ({ selection, pluginName, clipPathID, seriesLabels$, gridContainerPosition$, gridAxesTransform$, gridGraphicTransform$ }: {
30
+ selection: d3.Selection<any, unknown, any, unknown>
31
+ pluginName: string
32
+ clipPathID: string
33
+ // computedData$: Observable<ComputedDataGrid>
34
+ seriesLabels$: Observable<string[]>
35
+ gridContainerPosition$: Observable<GridContainerPosition[]>
36
+ gridAxesTransform$: Observable<TransformData>
37
+ gridGraphicTransform$: Observable<TransformData>
38
+ }) => {
39
+ const seriesClassName = getClassName(pluginName, 'series')
40
+ const axesClassName = getClassName(pluginName, 'axes')
41
+ const graphicClassName = getClassName(pluginName, 'graphic')
42
+
43
+ const seriesSelection$ = seriesLabels$.pipe(
44
+ map((existSeriesLabels, i) => {
45
+ return selection
46
+ .selectAll<SVGGElement, string>(`g.${seriesClassName}`)
47
+ .data(existSeriesLabels, d => d)
48
+ .join(
49
+ enter => {
50
+ return enter
51
+ .append('g')
52
+ .classed(seriesClassName, true)
53
+ .each((d, i, g) => {
54
+ const axesSelection = d3.select(g[i])
55
+ .selectAll<SVGGElement, ComputedDatumGrid[]>(`g.${axesClassName}`)
56
+ .data([i])
57
+ .join(
58
+ enter => {
59
+ return enter
60
+ .append('g')
61
+ .classed(axesClassName, true)
62
+ .attr('clip-path', `url(#${clipPathID})`)
63
+ .each((d, i, g) => {
64
+ const defsSelection = d3.select(g[i])
65
+ .selectAll<SVGDefsElement, any>('defs')
66
+ .data([i])
67
+ .join('defs')
68
+
69
+ const graphicGSelection = d3.select(g[i])
70
+ .selectAll<SVGGElement, any>('g')
71
+ .data([i])
72
+ .join('g')
73
+ .classed(graphicClassName, true)
74
+ })
75
+ },
76
+ update => update,
77
+ exit => exit.remove()
78
+ )
79
+ })
80
+ },
81
+ update => update,
82
+ exit => exit.remove()
83
+ )
84
+ }),
85
+ shareReplay(1)
86
+ )
87
+
88
+ combineLatest({
89
+ seriesSelection: seriesSelection$,
90
+ gridContainerPosition: gridContainerPosition$
91
+ }).pipe(
92
+ switchMap(async d => d)
93
+ ).subscribe(data => {
94
+ data.seriesSelection
95
+ .transition()
96
+ .attr('transform', (d, i) => {
97
+ const gridContainerPosition = data.gridContainerPosition[i] ?? data.gridContainerPosition[0]
98
+ const translate = gridContainerPosition.translate
99
+ const scale = gridContainerPosition.scale
100
+ return `translate(${translate[0]}, ${translate[1]}) scale(${scale[0]}, ${scale[1]})`
101
+ })
102
+ })
103
+
104
+ const axesSelection$ = combineLatest({
105
+ seriesSelection: seriesSelection$,
106
+ gridAxesTransform: gridAxesTransform$
107
+ }).pipe(
108
+ switchMap(async d => d),
109
+ map(data => {
110
+ return data.seriesSelection
111
+ .select<SVGGElement>(`g.${axesClassName}`)
112
+ .style('transform', data.gridAxesTransform.value)
113
+ }),
114
+ shareReplay(1)
115
+ )
116
+ const defsSelection$ = axesSelection$.pipe(
117
+ map(axesSelection => {
118
+ return axesSelection.select<SVGDefsElement>('defs')
119
+ }),
120
+ shareReplay(1)
121
+ )
122
+ const graphicGSelection$ = combineLatest({
123
+ axesSelection: axesSelection$,
124
+ gridGraphicTransform: gridGraphicTransform$
125
+ }).pipe(
126
+ switchMap(async d => d),
127
+ map(data => {
128
+ const graphicGSelection = data.axesSelection
129
+ .select<SVGGElement>(`g.${graphicClassName}`)
130
+ graphicGSelection
131
+ .transition()
132
+ .duration(50)
133
+ .style('transform', data.gridGraphicTransform.value)
134
+ return graphicGSelection
135
+ }),
136
+ shareReplay(1)
137
+ )
138
+
139
+ return {
140
+ seriesSelection$,
141
+ axesSelection$,
142
+ defsSelection$,
143
+ graphicGSelection$
144
+ }
145
+ }
146
+
147
+ // 由事件取得group data的function
148
+ // @Q@ 之後重構改用 gridGroupPosition
149
+ export const gridGroupPositionFnObservable = ({ fullDataFormatter$, gridAxesSize$, computedData$, fullChartParams$, gridContainerPosition$, layout$ }: {
150
+ fullDataFormatter$: Observable<DataFormatterGrid>
151
+ gridAxesSize$: Observable<{
152
+ width: number;
153
+ height: number;
154
+ }>
155
+ computedData$: Observable<ComputedDataGrid>
156
+ // GroupDataMap$: Observable<Map<string, ComputedDatumGrid[]>>
157
+ fullChartParams$: Observable<ChartParams>
158
+ gridContainerPosition$: Observable<GridContainerPosition[]>
159
+ layout$: Observable<Layout>
160
+ }): Observable<(event: any) => { groupIndex: number; groupLabel: string }> => {
161
+ const destroy$ = new Subject()
162
+
163
+ // 顯示範圍內的group labels
164
+ // const scaleRangeGroupLabels$: Observable<string[]> = new Observable(subscriber => {
165
+ // combineLatest({
166
+ // dataFormatter: fullDataFormatter$,
167
+ // computedData: computedData$
168
+ // }).pipe(
169
+ // takeUntil(destroy$),
170
+ // switchMap(async (d) => d),
171
+ // ).subscribe(data => {
172
+ // const groupMin = 0
173
+ // const groupMax = data.computedData[0] ? data.computedData[0].length - 1 : 0
174
+ // const groupScaleDomainMin = data.dataFormatter.grid.groupAxis.scaleDomain[0] === 'auto'
175
+ // ? groupMin - data.dataFormatter.grid.groupAxis.scalePadding
176
+ // : data.dataFormatter.grid.groupAxis.scaleDomain[0] as number - data.dataFormatter.grid.groupAxis.scalePadding
177
+ // const groupScaleDomainMax = data.dataFormatter.grid.groupAxis.scaleDomain[1] === 'auto'
178
+ // ? groupMax + data.dataFormatter.grid.groupAxis.scalePadding
179
+ // : data.dataFormatter.grid.groupAxis.scaleDomain[1] as number + data.dataFormatter.grid.groupAxis.scalePadding
180
+
181
+ // // const groupingAmount = data.computedData[0]
182
+ // // ? data.computedData[0].length
183
+ // // : 0
184
+
185
+ // let _labels = data.dataFormatter.grid.seriesDirection === 'row'
186
+ // ? (data.computedData[0] ?? []).map(d => d.groupLabel)
187
+ // : data.computedData.map(d => d[0].groupLabel)
188
+
189
+ // const _axisLabels =
190
+ // // new Array(groupingAmount).fill(0)
191
+ // // .map((d, i) => {
192
+ // // return _labels[i] != null
193
+ // // ? _labels[i]
194
+ // // : String(i) // 沒有label則用序列號填充
195
+ // // })
196
+ // _labels
197
+ // .filter((d, i) => {
198
+ // return i >= groupScaleDomainMin && i <= groupScaleDomainMax
199
+ // })
200
+ // subscriber.next(_axisLabels)
201
+ // })
202
+ // })
203
+ const groupScaleDomain$ = combineLatest({
204
+ fullDataFormatter: fullDataFormatter$,
205
+ gridAxesSize: gridAxesSize$,
206
+ computedData: computedData$
207
+ }).pipe(
208
+ switchMap(async (d) => d),
209
+ map(data => {
210
+ const groupMin = 0
211
+ const groupMax = data.computedData[0] ? data.computedData[0].length - 1 : 0
212
+ const groupScaleDomainMin = data.fullDataFormatter.grid.groupAxis.scaleDomain[0] === 'auto'
213
+ ? groupMin - data.fullDataFormatter.grid.groupAxis.scalePadding
214
+ : data.fullDataFormatter.grid.groupAxis.scaleDomain[0] as number - data.fullDataFormatter.grid.groupAxis.scalePadding
215
+ const groupScaleDomainMax = data.fullDataFormatter.grid.groupAxis.scaleDomain[1] === 'auto'
216
+ ? groupMax + data.fullDataFormatter.grid.groupAxis.scalePadding
217
+ : data.fullDataFormatter.grid.groupAxis.scaleDomain[1] as number + data.fullDataFormatter.grid.groupAxis.scalePadding
218
+
219
+ return [groupScaleDomainMin, groupScaleDomainMax]
220
+ }),
221
+ shareReplay(1)
222
+ )
223
+
224
+ const groupLabels$ = combineLatest({
225
+ fullDataFormatter: fullDataFormatter$,
226
+ computedData: computedData$
227
+ }).pipe(
228
+ switchMap(async d => d),
229
+ map(data => {
230
+ return data.fullDataFormatter.grid.seriesDirection === 'row'
231
+ ? (data.computedData[0] ?? []).map(d => d.groupLabel)
232
+ : data.computedData.map(d => d[0].groupLabel)
233
+ })
234
+ )
235
+
236
+ // 顯示範圍內的group labels
237
+ const scaleRangeGroupLabels$ = combineLatest({
238
+ groupScaleDomain: groupScaleDomain$,
239
+ groupLabels: groupLabels$
240
+ }).pipe(
241
+ switchMap(async d => d),
242
+ map(data => {
243
+ return data.groupLabels
244
+ .filter((d, i) => {
245
+ return i >= data.groupScaleDomain[0] && i <= data.groupScaleDomain[1]
246
+ })
247
+ })
248
+ )
249
+
250
+ const columnAmount$ = gridContainerPosition$.pipe(
251
+ map(gridContainerPosition => {
252
+ const maxColumnIndex = gridContainerPosition.reduce((acc, current) => {
253
+ return current.columnIndex > acc ? current.columnIndex : acc
254
+ }, 0)
255
+ return maxColumnIndex + 1
256
+ }),
257
+ distinctUntilChanged()
258
+ )
259
+
260
+ const rowAmount$ = gridContainerPosition$.pipe(
261
+ map(gridContainerPosition => {
262
+ const maxRowIndex = gridContainerPosition.reduce((acc, current) => {
263
+ return current.rowIndex > acc ? current.rowIndex : acc
264
+ }, 0)
265
+ return maxRowIndex + 1
266
+ }),
267
+ distinctUntilChanged()
268
+ )
269
+
270
+ return new Observable<(event: any) => { groupIndex: number; groupLabel: string }>(subscriber => {
271
+ combineLatest({
272
+ dataFormatter: fullDataFormatter$,
273
+ axisSize: gridAxesSize$,
274
+ fullChartParams: fullChartParams$,
275
+ scaleRangeGroupLabels: scaleRangeGroupLabels$,
276
+ groupScaleDomain: groupScaleDomain$,
277
+ columnAmount: columnAmount$,
278
+ rowAmount: rowAmount$,
279
+ layout: layout$
280
+ }).pipe(
281
+ takeUntil(destroy$),
282
+ switchMap(async (d) => d),
283
+ ).subscribe(data => {
284
+
285
+ const reverse = data.dataFormatter.grid.valueAxis.position === 'right'
286
+ || data.dataFormatter.grid.valueAxis.position === 'bottom'
287
+ ? true : false
288
+
289
+ // 比例尺座標對應非連續資料索引
290
+ const xIndexScale = createAxisQuantizeScale({
291
+ axisLabels: data.scaleRangeGroupLabels,
292
+ axisWidth: data.axisSize.width,
293
+ reverse
294
+ })
295
+
296
+ // 依比例尺位置計算座標
297
+ const axisValuePredicate = (event: any) => {
298
+ return data.dataFormatter.grid.groupAxis.position === 'bottom'
299
+ || data.dataFormatter.grid.groupAxis.position === 'top'
300
+ ? event.offsetX - data.fullChartParams.padding.left
301
+ : event.offsetY - data.fullChartParams.padding.top
302
+ }
303
+
304
+ // 比例尺座標取得groupData的function
305
+ const createEventGroupData: (event: any) => { groupIndex: number; groupLabel: string } = (event: any) => {
306
+ // 由於event座標是基於底層的,但是container會有多欄,所以要重新計算
307
+ const eventData = {
308
+ offsetX: event.offsetX * data.columnAmount % data.layout.rootWidth,
309
+ offsetY: event.offsetY * data.rowAmount % data.layout.rootHeight
310
+ }
311
+ // console.log('data.columnAmount', data.columnAmount, 'data.rowAmount', data.rowAmount, 'data.layout.rootWidth', data.layout.rootWidth, 'data.layout.rootHeight', data.layout.rootHeight)
312
+ const axisValue = axisValuePredicate(eventData)
313
+ const xIndex = xIndexScale(axisValue)
314
+ const currentxIndexStart = Math.ceil(data.groupScaleDomain[0]) // 因為有padding所以會有小數點,所以要無條件進位
315
+ const groupIndex = xIndex + currentxIndexStart
316
+ return {
317
+ groupIndex,
318
+ groupLabel: data.scaleRangeGroupLabels[groupIndex] ?? ''
319
+ }
320
+ }
321
+
322
+ subscriber.next(createEventGroupData)
323
+
324
+ return function unsubscribe () {
325
+ destroy$.next(undefined)
326
+ }
327
+ })
328
+ })
329
+ }
330
+
331
+ export const gridGroupPosition = ({ rootSelection, fullDataFormatter$, gridAxesSize$, computedData$, fullChartParams$, gridContainerPosition$, layout$ }: {
332
+ rootSelection: d3.Selection<any, unknown, any, unknown>
333
+ fullDataFormatter$: Observable<DataFormatterGrid>
334
+ gridAxesSize$: Observable<{
335
+ width: number;
336
+ height: number;
337
+ }>
338
+ computedData$: Observable<ComputedDataGrid>
339
+ fullChartParams$: Observable<ChartParams>
340
+ gridContainerPosition$: Observable<GridContainerPosition[]>
341
+ layout$: Observable<Layout>
342
+ }) => {
343
+ const rootMousemove$: Observable<any> = d3EventObservable(rootSelection, 'mousemove')
344
+
345
+ const groupScaleDomain$ = combineLatest({
346
+ fullDataFormatter: fullDataFormatter$,
347
+ gridAxesSize: gridAxesSize$,
348
+ computedData: computedData$
349
+ }).pipe(
350
+ switchMap(async (d) => d),
351
+ map(data => {
352
+ const groupMin = 0
353
+ const groupMax = data.computedData[0] ? data.computedData[0].length - 1 : 0
354
+ const groupScaleDomainMin = data.fullDataFormatter.grid.groupAxis.scaleDomain[0] === 'auto'
355
+ ? groupMin - data.fullDataFormatter.grid.groupAxis.scalePadding
356
+ : data.fullDataFormatter.grid.groupAxis.scaleDomain[0] as number - data.fullDataFormatter.grid.groupAxis.scalePadding
357
+ const groupScaleDomainMax = data.fullDataFormatter.grid.groupAxis.scaleDomain[1] === 'auto'
358
+ ? groupMax + data.fullDataFormatter.grid.groupAxis.scalePadding
359
+ : data.fullDataFormatter.grid.groupAxis.scaleDomain[1] as number + data.fullDataFormatter.grid.groupAxis.scalePadding
360
+
361
+ return [groupScaleDomainMin, groupScaleDomainMax]
362
+ }),
363
+ shareReplay(1)
364
+ )
365
+
366
+ const groupLabels$ = combineLatest({
367
+ fullDataFormatter: fullDataFormatter$,
368
+ computedData: computedData$
369
+ }).pipe(
370
+ switchMap(async d => d),
371
+ map(data => {
372
+ return data.fullDataFormatter.grid.seriesDirection === 'row'
373
+ ? (data.computedData[0] ?? []).map(d => d.groupLabel)
374
+ : data.computedData.map(d => d[0].groupLabel)
375
+ })
376
+ )
377
+
378
+ const scaleRangeGroupLabels$ = combineLatest({
379
+ groupScaleDomain: groupScaleDomain$,
380
+ groupLabels: groupLabels$
381
+ }).pipe(
382
+ switchMap(async d => d),
383
+ map(data => {
384
+ return data.groupLabels
385
+ .filter((d, i) => {
386
+ return i >= data.groupScaleDomain[0] && i <= data.groupScaleDomain[1]
387
+ })
388
+ })
389
+ )
390
+
391
+ const reverse$ = fullDataFormatter$.pipe(
392
+ map(d => {
393
+ return d.grid.valueAxis.position === 'right' || d.grid.valueAxis.position === 'bottom'
394
+ ? true
395
+ : false
396
+ })
397
+ )
398
+
399
+ // 比例尺座標對應非連續資料索引
400
+ const xIndexScale$ = combineLatest({
401
+ reverse: reverse$,
402
+ gridAxesSize: gridAxesSize$,
403
+ scaleRangeGroupLabels: scaleRangeGroupLabels$
404
+ }).pipe(
405
+ switchMap(async d => d),
406
+ map(data => {
407
+ return createAxisQuantizeScale({
408
+ axisLabels: data.scaleRangeGroupLabels,
409
+ axisWidth: data.gridAxesSize.width,
410
+ reverse: data.reverse
411
+ })
412
+ })
413
+ )
414
+
415
+ const columnAmount$ = gridContainerPosition$.pipe(
416
+ map(gridContainerPosition => {
417
+ const maxColumnIndex = gridContainerPosition.reduce((acc, current) => {
418
+ return current.columnIndex > acc ? current.columnIndex : acc
419
+ }, 0)
420
+ return maxColumnIndex + 1
421
+ }),
422
+ distinctUntilChanged()
423
+ )
424
+
425
+ const rowAmount$ = gridContainerPosition$.pipe(
426
+ map(gridContainerPosition => {
427
+ const maxRowIndex = gridContainerPosition.reduce((acc, current) => {
428
+ return current.rowIndex > acc ? current.rowIndex : acc
429
+ }, 0)
430
+ return maxRowIndex + 1
431
+ }),
432
+ distinctUntilChanged()
433
+ )
434
+
435
+ const axisValue$ = combineLatest({
436
+ fullDataFormatter: fullDataFormatter$,
437
+ fullChartParams: fullChartParams$,
438
+ rootMousemove: rootMousemove$,
439
+ columnAmount: columnAmount$,
440
+ rowAmount: rowAmount$,
441
+ layout: layout$
442
+ }).pipe(
443
+ switchMap(async d => d),
444
+ map(data => {
445
+ // 由於event座標是基於底層的,但是container會有多欄,所以要重新計算
446
+ const eventData = {
447
+ offsetX: data.rootMousemove.offsetX * data.columnAmount % data.layout.rootWidth,
448
+ offsetY: data.rootMousemove.offsetY * data.rowAmount % data.layout.rootHeight
449
+ }
450
+ return data.fullDataFormatter.grid.groupAxis.position === 'bottom'
451
+ || data.fullDataFormatter.grid.groupAxis.position === 'top'
452
+ ? eventData.offsetX - data.fullChartParams.padding.left
453
+ : eventData.offsetY - data.fullChartParams.padding.top
454
+ })
455
+ )
456
+
457
+ const groupIndex$ = combineLatest({
458
+ xIndexScale: xIndexScale$,
459
+ axisValue: axisValue$,
460
+ groupScaleDomain: groupScaleDomain$
461
+ }).pipe(
462
+ switchMap(async d => d),
463
+ map(data => {
464
+ const xIndex = data.xIndexScale(data.axisValue)
465
+ const currentxIndexStart = Math.ceil(data.groupScaleDomain[0]) // 因為有padding所以會有小數點,所以要無條件進位
466
+ return xIndex + currentxIndexStart
467
+ })
468
+ )
469
+
470
+ const groupLabel$ = combineLatest({
471
+ groupIndex: groupIndex$,
472
+ groupLabels: groupLabels$
473
+ }).pipe(
474
+ switchMap(async d => d),
475
+ map(data => {
476
+ return data.groupLabels[data.groupIndex] ?? ''
477
+ })
478
+ )
479
+
480
+ return combineLatest({
481
+ groupIndex: groupIndex$,
482
+ groupLabel: groupLabel$
483
+ }).pipe(
484
+ switchMap(async d => d),
485
+ map(data => {
486
+ return {
487
+ groupIndex: data.groupIndex,
488
+ groupLabel: data.groupLabel
489
+ }
490
+ })
491
+ )
492
+ }
493
+
494
+ // const gridContainerEventData$ = ({ eventData$, gridContainerPosition$, layout$ }: {
495
+ // eventData$: Observable<any>
496
+ // gridContainerPosition$: Observable<GridContainerPosition[]>
497
+ // layout$: Observable<Layout>
498
+ // }): Observable<{
499
+ // offsetX: number;
500
+ // offsetY: number;
501
+ // }> => {
502
+ // const columnAmount$ = gridContainerPosition$.pipe(
503
+ // map(gridContainerPosition => {
504
+ // const maxColumnIndex = gridContainerPosition.reduce((acc, current) => {
505
+ // return current.columnIndex > acc ? current.columnIndex : acc
506
+ // }, 0)
507
+ // return maxColumnIndex + 1
508
+ // }),
509
+ // distinctUntilChanged()
510
+ // )
511
+
512
+ // const rowAmount$ = gridContainerPosition$.pipe(
513
+ // map(gridContainerPosition => {
514
+ // const maxRowIndex = gridContainerPosition.reduce((acc, current) => {
515
+ // return current.rowIndex > acc ? current.rowIndex : acc
516
+ // }, 0)
517
+ // return maxRowIndex + 1
518
+ // }),
519
+ // distinctUntilChanged()
520
+ // )
521
+
522
+ // return combineLatest({
523
+ // eventData: eventData$,
524
+ // gridContainerPosition: gridContainerPosition$,
525
+ // layout: layout$,
526
+ // columnAmount: columnAmount$,
527
+ // rowAmount: rowAmount$
528
+ // }).pipe(
529
+ // switchMap(async d => d),
530
+ // map(data => {
531
+ // // 由於event座標是基於底層的,但是container會有多欄,所以要重新計算
532
+ // const eventData = {
533
+ // offsetX: data.eventData.offsetX * data.columnAmount % data.layout.rootWidth,
534
+ // offsetY: data.eventData.offsetY * data.rowAmount % data.layout.rootHeight
535
+ // }
536
+ // return eventData
537
+ // })
538
+ // )
539
+ // }