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

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.
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
+ // }