@orbcharts/plugins-basic 3.0.0-beta.10 → 3.0.0-beta.11

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 (99) hide show
  1. package/LICENSE +200 -200
  2. package/dist/orbcharts-plugins-basic.es.js +177 -177
  3. package/dist/orbcharts-plugins-basic.umd.js +55 -55
  4. package/lib/core-types.ts +7 -7
  5. package/lib/core.ts +6 -6
  6. package/lib/plugins-basic-types.ts +6 -6
  7. package/package.json +44 -44
  8. package/src/base/BaseBars.ts +765 -765
  9. package/src/base/BaseBarsTriangle.ts +676 -676
  10. package/src/base/BaseDots.ts +464 -464
  11. package/src/base/BaseGroupAxis.ts +679 -679
  12. package/src/base/BaseLegend.ts +684 -684
  13. package/src/base/BaseLineAreas.ts +629 -629
  14. package/src/base/BaseLines.ts +706 -706
  15. package/src/base/BaseStackedBar.ts +782 -782
  16. package/src/base/BaseTooltip.ts +385 -385
  17. package/src/base/BaseValueAxis.ts +583 -583
  18. package/src/base/types.ts +2 -2
  19. package/src/const.ts +30 -30
  20. package/src/grid/defaults.ts +246 -246
  21. package/src/grid/gridObservables.ts +554 -554
  22. package/src/grid/index.ts +16 -16
  23. package/src/grid/plugins/Bars.ts +69 -69
  24. package/src/grid/plugins/BarsPN.ts +66 -66
  25. package/src/grid/plugins/BarsTriangle.ts +73 -73
  26. package/src/grid/plugins/Dots.ts +68 -68
  27. package/src/grid/plugins/GridLegend.ts +107 -107
  28. package/src/grid/plugins/GridTooltip.ts +66 -66
  29. package/src/grid/plugins/GridZoom.ts +218 -218
  30. package/src/grid/plugins/GroupAux.ts +1103 -1103
  31. package/src/grid/plugins/GroupAxis.ts +97 -97
  32. package/src/grid/plugins/LineAreas.ts +65 -65
  33. package/src/grid/plugins/Lines.ts +59 -59
  34. package/src/grid/plugins/StackedBar.ts +64 -64
  35. package/src/grid/plugins/StackedValueAxis.ts +96 -96
  36. package/src/grid/plugins/ValueAxis.ts +94 -94
  37. package/src/index.ts +6 -6
  38. package/src/multiGrid/defaults.ts +224 -224
  39. package/src/multiGrid/index.ts +14 -14
  40. package/src/multiGrid/multiGridObservables.ts +49 -49
  41. package/src/multiGrid/plugins/MultiBars.ts +108 -108
  42. package/src/multiGrid/plugins/MultiBarsTriangle.ts +114 -114
  43. package/src/multiGrid/plugins/MultiDots.ts +102 -102
  44. package/src/multiGrid/plugins/MultiGridLegend.ts +159 -159
  45. package/src/multiGrid/plugins/MultiGridTooltip.ts +66 -66
  46. package/src/multiGrid/plugins/MultiGroupAxis.ts +137 -137
  47. package/src/multiGrid/plugins/MultiLineAreas.ts +107 -107
  48. package/src/multiGrid/plugins/MultiLines.ts +101 -101
  49. package/src/multiGrid/plugins/MultiStackedBar.ts +106 -106
  50. package/src/multiGrid/plugins/MultiStackedValueAxis.ts +134 -134
  51. package/src/multiGrid/plugins/MultiValueAxis.ts +134 -134
  52. package/src/multiGrid/plugins/OverlappingStackedValueAxes.ts +299 -299
  53. package/src/multiGrid/plugins/OverlappingValueAxes.ts +300 -300
  54. package/src/multiValue/defaults.ts +166 -166
  55. package/src/multiValue/index.ts +8 -8
  56. package/src/multiValue/multiValueObservables.ts +297 -297
  57. package/src/multiValue/plugins/MultiValueLegend.ts +107 -107
  58. package/src/multiValue/plugins/MultiValueTooltip.ts +66 -66
  59. package/src/multiValue/plugins/Scatter.ts +426 -426
  60. package/src/multiValue/plugins/ScatterBubbles.ts +554 -554
  61. package/src/multiValue/plugins/XYAux.ts +681 -681
  62. package/src/multiValue/plugins/XYAxes.ts +684 -684
  63. package/src/multiValue/plugins/XYZoom.ts +299 -299
  64. package/src/noneData/defaults.ts +102 -102
  65. package/src/noneData/index.ts +3 -3
  66. package/src/noneData/plugins/Container.ts +27 -27
  67. package/src/noneData/plugins/Tooltip.ts +373 -373
  68. package/src/relationship/defaults.ts +196 -195
  69. package/src/relationship/index.ts +5 -5
  70. package/src/relationship/plugins/ForceDirected.ts +1168 -1166
  71. package/src/relationship/plugins/ForceDirectedBubbles.ts +1393 -1390
  72. package/src/relationship/plugins/RelationshipLegend.ts +100 -100
  73. package/src/relationship/plugins/RelationshipTooltip.ts +66 -66
  74. package/src/relationship/relationshipObservables.ts +49 -49
  75. package/src/series/defaults.ts +207 -206
  76. package/src/series/index.ts +9 -9
  77. package/src/series/plugins/Bubbles.ts +603 -606
  78. package/src/series/plugins/Pie.ts +623 -623
  79. package/src/series/plugins/PieEventTexts.ts +284 -284
  80. package/src/series/plugins/PieLabels.ts +640 -640
  81. package/src/series/plugins/Rose.ts +516 -516
  82. package/src/series/plugins/RoseLabels.ts +600 -600
  83. package/src/series/plugins/SeriesLegend.ts +107 -107
  84. package/src/series/plugins/SeriesTooltip.ts +66 -66
  85. package/src/series/seriesObservables.ts +145 -145
  86. package/src/series/seriesUtils.ts +51 -51
  87. package/src/tree/defaults.ts +78 -78
  88. package/src/tree/index.ts +4 -4
  89. package/src/tree/plugins/TreeLegend.ts +100 -100
  90. package/src/tree/plugins/TreeMap.ts +333 -333
  91. package/src/tree/plugins/TreeTooltip.ts +66 -66
  92. package/src/utils/commonUtils.ts +21 -21
  93. package/src/utils/d3Graphics.ts +174 -174
  94. package/src/utils/d3Utils.ts +74 -74
  95. package/src/utils/observables.ts +14 -14
  96. package/src/utils/orbchartsUtils.ts +115 -115
  97. package/tsconfig.base.json +13 -13
  98. package/tsconfig.json +2 -2
  99. package/vite.config.js +22 -22
@@ -1,297 +1,297 @@
1
- import * as d3 from 'd3'
2
- import {
3
- Observable,
4
- Subject,
5
- debounceTime,
6
- of,
7
- takeUntil,
8
- filter,
9
- map,
10
- switchMap,
11
- combineLatest,
12
- merge,
13
- shareReplay,
14
- distinctUntilChanged
15
- } from 'rxjs'
16
- import type {
17
- ChartParams,
18
- HighlightTarget,
19
- DataFormatterMultiValue,
20
- ComputedDataMultiValue,
21
- ComputedDatumMultiValue,
22
- ComputedLayoutDatumMultiValue,
23
- TransformData,
24
- ContainerPositionScaled,
25
- Layout,
26
- } from '../../lib/core-types'
27
- import { createAxisToLabelIndexScale, createAxisToValueScale } from '../../lib/core'
28
- import { getClassName, getUniID } from '../utils/orbchartsUtils'
29
- import { d3EventObservable } from '../utils/observables'
30
-
31
- // 建立 multiValue 主要的 selection
32
- export const multiValueSelectionsObservable = ({ selection, pluginName, clipPathID, categoryLabels$, multiValueContainerPosition$, multiValueGraphicTransform$ }: {
33
- selection: d3.Selection<any, unknown, any, unknown>
34
- pluginName: string
35
- clipPathID: string
36
- // computedData$: Observable<ComputedDataMultiValue>
37
- categoryLabels$: Observable<string[]>
38
- multiValueContainerPosition$: Observable<ContainerPositionScaled[]>
39
- // multiValueAxesTransform$: Observable<TransformData>
40
- multiValueGraphicTransform$: Observable<TransformData>
41
- }) => {
42
- const categoryClassName = getClassName(pluginName, 'category')
43
- const axesClassName = getClassName(pluginName, 'axes')
44
- const graphicClassName = getClassName(pluginName, 'graphic')
45
-
46
- // <g> category selection(container排放位置)
47
- // <g> axes selection(圖軸)
48
- // <defs> clipPath selection
49
- // <g> graphic selection(圖形 scale 範圍的變形)
50
- const categorySelection$ = categoryLabels$.pipe(
51
- map((categoryLabels, i) => {
52
- return selection
53
- .selectAll<SVGGElement, string>(`g.${categoryClassName}`)
54
- .data(categoryLabels, d => d)
55
- .join(
56
- enter => {
57
- return enter
58
- .append('g')
59
- .classed(categoryClassName, true)
60
- .each((d, i, g) => {
61
- const axesSelection = d3.select(g[i])
62
- .selectAll<SVGGElement, ComputedDatumMultiValue[]>(`g.${axesClassName}`)
63
- .data([i])
64
- .join(
65
- enter => {
66
- return enter
67
- .append('g')
68
- .classed(axesClassName, true)
69
- .attr('clip-path', `url(#${clipPathID})`)
70
- .each((d, i, g) => {
71
- const defsSelection = d3.select(g[i])
72
- .selectAll<SVGDefsElement, any>('defs')
73
- .data([i])
74
- .join('defs')
75
-
76
- const graphicGSelection = d3.select(g[i])
77
- .selectAll<SVGGElement, any>('g')
78
- .data([i])
79
- .join('g')
80
- .classed(graphicClassName, true)
81
- })
82
- },
83
- update => update,
84
- exit => exit.remove()
85
- )
86
- })
87
- },
88
- update => update,
89
- exit => exit.remove()
90
- )
91
- }),
92
- shareReplay(1)
93
- )
94
-
95
- // <g> category selection
96
- combineLatest({
97
- categorySelection: categorySelection$,
98
- multiValueContainerPosition: multiValueContainerPosition$
99
- }).pipe(
100
- switchMap(async d => d)
101
- ).subscribe(data => {
102
- data.categorySelection
103
- .transition()
104
- .attr('transform', (d, i) => {
105
- const multiValueContainerPosition = data.multiValueContainerPosition[i] ?? data.multiValueContainerPosition[0]
106
- const translate = multiValueContainerPosition.translate
107
- const scale = multiValueContainerPosition.scale
108
- return `translate(${translate[0]}, ${translate[1]}) scale(${scale[0]}, ${scale[1]})`
109
- })
110
- })
111
-
112
- // <g> axes selection
113
- const axesSelection$ = categorySelection$.pipe(
114
- map(categorySelection => {
115
- return categorySelection
116
- .select<SVGGElement>(`g.${axesClassName}`)
117
- }),
118
- shareReplay(1)
119
- )
120
-
121
- // <defs> clipPath selection
122
- const defsSelection$ = axesSelection$.pipe(
123
- map(axesSelection => {
124
- return axesSelection.select<SVGDefsElement>('defs')
125
- }),
126
- shareReplay(1)
127
- )
128
-
129
- // <g> graphic selection
130
- const graphicGSelection$ = combineLatest({
131
- axesSelection: axesSelection$,
132
- multiValueGraphicTransform: multiValueGraphicTransform$
133
- }).pipe(
134
- switchMap(async d => d),
135
- map(data => {
136
- const graphicGSelection = data.axesSelection
137
- .select<SVGGElement>(`g.${graphicClassName}`)
138
- graphicGSelection
139
- .transition()
140
- .duration(50)
141
- .style('transform', data.multiValueGraphicTransform.value)
142
- return graphicGSelection
143
- }),
144
- shareReplay(1)
145
- )
146
-
147
- return {
148
- categorySelection$,
149
- axesSelection$,
150
- defsSelection$,
151
- graphicGSelection$
152
- }
153
- }
154
-
155
-
156
- export const multiValueXYPositionObservable = ({ rootSelection, fullDataFormatter$, filteredMinMaxXYData$, multiValueContainerPosition$, layout$ }: {
157
- rootSelection: d3.Selection<any, unknown, any, unknown>
158
- fullDataFormatter$: Observable<DataFormatterMultiValue>
159
- // computedData$: Observable<ComputedDataMultiValue>
160
- // minMaxXY$: Observable<{ minX: number, maxX: number, minY: number, maxY: number }>
161
- filteredMinMaxXYData$: Observable<{
162
- minXDatum: ComputedLayoutDatumMultiValue
163
- maxXDatum: ComputedLayoutDatumMultiValue
164
- minYDatum: ComputedLayoutDatumMultiValue
165
- maxYDatum: ComputedLayoutDatumMultiValue
166
- }>
167
- multiValueContainerPosition$: Observable<ContainerPositionScaled[]>
168
- layout$: Observable<Layout>
169
- }) => {
170
- const rootMousemove$ = d3EventObservable(rootSelection, 'mousemove').pipe(
171
- debounceTime(2) // 避免過度頻繁觸發,實測時沒加電腦容易卡頓
172
- )
173
-
174
- const columnAmount$ = multiValueContainerPosition$.pipe(
175
- map(multiValueContainerPosition => {
176
- const maxColumnIndex = multiValueContainerPosition.reduce((acc, current) => {
177
- return current.columnIndex > acc ? current.columnIndex : acc
178
- }, 0)
179
- return maxColumnIndex + 1
180
- }),
181
- distinctUntilChanged(),
182
- shareReplay(1)
183
- )
184
-
185
- const rowAmount$ = multiValueContainerPosition$.pipe(
186
- map(multiValueContainerPosition => {
187
- const maxRowIndex = multiValueContainerPosition.reduce((acc, current) => {
188
- return current.rowIndex > acc ? current.rowIndex : acc
189
- }, 0)
190
- return maxRowIndex + 1
191
- }),
192
- distinctUntilChanged(),
193
- shareReplay(1)
194
- )
195
-
196
- // const xyScale$ = combineLatest({
197
- // layout: layout$,
198
- // filteredMinMaxXYData: filteredMinMaxXYData$,
199
- // fullDataFormatter: fullDataFormatter$,
200
- // columnAmount: columnAmount$,
201
- // rowAmount: rowAmount$
202
- // }).pipe(
203
- // switchMap(async d => d),
204
- // map(data => {
205
- // const xScale = createAxisToValueScale({
206
- // maxValue: data.filteredMinMaxXYData.maxXDatum.value[0],
207
- // minValue: data.filteredMinMaxXYData.minXDatum.value[0],
208
- // axisWidth: data.layout.width,
209
- // scaleDomain: data.fullDataFormatter.xAxis.scaleDomain,
210
- // scaleRange: data.fullDataFormatter.xAxis.scaleRange,
211
- // })
212
- // const yScale = createAxisToValueScale({
213
- // maxValue: data.filteredMinMaxXYData.maxYDatum.value[1],
214
- // minValue: data.filteredMinMaxXYData.minYDatum.value[1],
215
- // axisWidth: data.layout.height,
216
- // scaleDomain: data.fullDataFormatter.yAxis.scaleDomain,
217
- // scaleRange: data.fullDataFormatter.yAxis.scaleRange,
218
- // reverse: true
219
- // })
220
- // return { xScale, yScale }
221
- // })
222
- // )
223
-
224
- const xyScale$: Observable<{
225
- xScale: d3.ScaleLinear<number, number, never>;
226
- yScale: d3.ScaleLinear<number, number, never>;
227
- }> = new Observable(subscriber => {
228
- combineLatest({
229
- layout: layout$,
230
- filteredMinMaxXYData: filteredMinMaxXYData$,
231
- fullDataFormatter: fullDataFormatter$,
232
- columnAmount: columnAmount$,
233
- rowAmount: rowAmount$
234
- }).pipe(
235
- switchMap(async d => d),
236
- ).subscribe(data => {
237
- if (!data.filteredMinMaxXYData.minXDatum || !data.filteredMinMaxXYData.maxXDatum
238
- || data.filteredMinMaxXYData.minXDatum.value[0] == null || data.filteredMinMaxXYData.maxXDatum.value[0] == null
239
- || !data.filteredMinMaxXYData.minYDatum || !data.filteredMinMaxXYData.maxYDatum
240
- || data.filteredMinMaxXYData.minYDatum.value[1] == null || data.filteredMinMaxXYData.maxYDatum.value[1] == null
241
- ) {
242
- return
243
- }
244
- const xScale = createAxisToValueScale({
245
- maxValue: data.filteredMinMaxXYData.maxXDatum.value[0],
246
- minValue: data.filteredMinMaxXYData.minXDatum.value[0],
247
- axisWidth: data.layout.width,
248
- scaleDomain: data.fullDataFormatter.xAxis.scaleDomain,
249
- scaleRange: data.fullDataFormatter.xAxis.scaleRange,
250
- })
251
- const yScale = createAxisToValueScale({
252
- maxValue: data.filteredMinMaxXYData.maxYDatum.value[1],
253
- minValue: data.filteredMinMaxXYData.minYDatum.value[1],
254
- axisWidth: data.layout.height,
255
- scaleDomain: data.fullDataFormatter.yAxis.scaleDomain,
256
- scaleRange: data.fullDataFormatter.yAxis.scaleRange,
257
- reverse: true
258
- })
259
- subscriber.next({ xScale, yScale })
260
- })
261
- })
262
-
263
- const axisValue$ = combineLatest({
264
- fullDataFormatter: fullDataFormatter$,
265
- rootMousemove: rootMousemove$,
266
- columnAmount: columnAmount$,
267
- rowAmount: rowAmount$,
268
- layout: layout$,
269
- multiValueContainerPosition: multiValueContainerPosition$
270
- }).pipe(
271
- switchMap(async d => d),
272
- map(data => {
273
- // 由於event座標是基於底層的,但是container會有多欄,所以要重新計算
274
- return {
275
- x: ((data.rootMousemove.offsetX - data.layout.left) / data.multiValueContainerPosition[0].scale[0])
276
- % (data.layout.rootWidth / data.columnAmount / data.multiValueContainerPosition[0].scale[0]),
277
- y: ((data.rootMousemove.offsetY - data.layout.top) / data.multiValueContainerPosition[0].scale[1])
278
- % (data.layout.rootHeight / data.rowAmount / data.multiValueContainerPosition[0].scale[1])
279
- }
280
- })
281
- )
282
-
283
- return combineLatest({
284
- xyScale: xyScale$,
285
- axisValue: axisValue$
286
- }).pipe(
287
- switchMap(async d => d),
288
- map(data => {
289
- return {
290
- x: data.axisValue.x,
291
- y: data.axisValue.y,
292
- xValue: data.xyScale.xScale(data.axisValue.x),
293
- yValue: data.xyScale.yScale(data.axisValue.y)
294
- }
295
- })
296
- )
297
- }
1
+ import * as d3 from 'd3'
2
+ import {
3
+ Observable,
4
+ Subject,
5
+ debounceTime,
6
+ of,
7
+ takeUntil,
8
+ filter,
9
+ map,
10
+ switchMap,
11
+ combineLatest,
12
+ merge,
13
+ shareReplay,
14
+ distinctUntilChanged
15
+ } from 'rxjs'
16
+ import type {
17
+ ChartParams,
18
+ HighlightTarget,
19
+ DataFormatterMultiValue,
20
+ ComputedDataMultiValue,
21
+ ComputedDatumMultiValue,
22
+ ComputedLayoutDatumMultiValue,
23
+ TransformData,
24
+ ContainerPositionScaled,
25
+ Layout,
26
+ } from '../../lib/core-types'
27
+ import { createAxisToLabelIndexScale, createAxisToValueScale } from '../../lib/core'
28
+ import { getClassName, getUniID } from '../utils/orbchartsUtils'
29
+ import { d3EventObservable } from '../utils/observables'
30
+
31
+ // 建立 multiValue 主要的 selection
32
+ export const multiValueSelectionsObservable = ({ selection, pluginName, clipPathID, categoryLabels$, multiValueContainerPosition$, multiValueGraphicTransform$ }: {
33
+ selection: d3.Selection<any, unknown, any, unknown>
34
+ pluginName: string
35
+ clipPathID: string
36
+ // computedData$: Observable<ComputedDataMultiValue>
37
+ categoryLabels$: Observable<string[]>
38
+ multiValueContainerPosition$: Observable<ContainerPositionScaled[]>
39
+ // multiValueAxesTransform$: Observable<TransformData>
40
+ multiValueGraphicTransform$: Observable<TransformData>
41
+ }) => {
42
+ const categoryClassName = getClassName(pluginName, 'category')
43
+ const axesClassName = getClassName(pluginName, 'axes')
44
+ const graphicClassName = getClassName(pluginName, 'graphic')
45
+
46
+ // <g> category selection(container排放位置)
47
+ // <g> axes selection(圖軸)
48
+ // <defs> clipPath selection
49
+ // <g> graphic selection(圖形 scale 範圍的變形)
50
+ const categorySelection$ = categoryLabels$.pipe(
51
+ map((categoryLabels, i) => {
52
+ return selection
53
+ .selectAll<SVGGElement, string>(`g.${categoryClassName}`)
54
+ .data(categoryLabels, d => d)
55
+ .join(
56
+ enter => {
57
+ return enter
58
+ .append('g')
59
+ .classed(categoryClassName, true)
60
+ .each((d, i, g) => {
61
+ const axesSelection = d3.select(g[i])
62
+ .selectAll<SVGGElement, ComputedDatumMultiValue[]>(`g.${axesClassName}`)
63
+ .data([i])
64
+ .join(
65
+ enter => {
66
+ return enter
67
+ .append('g')
68
+ .classed(axesClassName, true)
69
+ .attr('clip-path', `url(#${clipPathID})`)
70
+ .each((d, i, g) => {
71
+ const defsSelection = d3.select(g[i])
72
+ .selectAll<SVGDefsElement, any>('defs')
73
+ .data([i])
74
+ .join('defs')
75
+
76
+ const graphicGSelection = d3.select(g[i])
77
+ .selectAll<SVGGElement, any>('g')
78
+ .data([i])
79
+ .join('g')
80
+ .classed(graphicClassName, true)
81
+ })
82
+ },
83
+ update => update,
84
+ exit => exit.remove()
85
+ )
86
+ })
87
+ },
88
+ update => update,
89
+ exit => exit.remove()
90
+ )
91
+ }),
92
+ shareReplay(1)
93
+ )
94
+
95
+ // <g> category selection
96
+ combineLatest({
97
+ categorySelection: categorySelection$,
98
+ multiValueContainerPosition: multiValueContainerPosition$
99
+ }).pipe(
100
+ switchMap(async d => d)
101
+ ).subscribe(data => {
102
+ data.categorySelection
103
+ .transition()
104
+ .attr('transform', (d, i) => {
105
+ const multiValueContainerPosition = data.multiValueContainerPosition[i] ?? data.multiValueContainerPosition[0]
106
+ const translate = multiValueContainerPosition.translate
107
+ const scale = multiValueContainerPosition.scale
108
+ return `translate(${translate[0]}, ${translate[1]}) scale(${scale[0]}, ${scale[1]})`
109
+ })
110
+ })
111
+
112
+ // <g> axes selection
113
+ const axesSelection$ = categorySelection$.pipe(
114
+ map(categorySelection => {
115
+ return categorySelection
116
+ .select<SVGGElement>(`g.${axesClassName}`)
117
+ }),
118
+ shareReplay(1)
119
+ )
120
+
121
+ // <defs> clipPath selection
122
+ const defsSelection$ = axesSelection$.pipe(
123
+ map(axesSelection => {
124
+ return axesSelection.select<SVGDefsElement>('defs')
125
+ }),
126
+ shareReplay(1)
127
+ )
128
+
129
+ // <g> graphic selection
130
+ const graphicGSelection$ = combineLatest({
131
+ axesSelection: axesSelection$,
132
+ multiValueGraphicTransform: multiValueGraphicTransform$
133
+ }).pipe(
134
+ switchMap(async d => d),
135
+ map(data => {
136
+ const graphicGSelection = data.axesSelection
137
+ .select<SVGGElement>(`g.${graphicClassName}`)
138
+ graphicGSelection
139
+ .transition()
140
+ .duration(50)
141
+ .style('transform', data.multiValueGraphicTransform.value)
142
+ return graphicGSelection
143
+ }),
144
+ shareReplay(1)
145
+ )
146
+
147
+ return {
148
+ categorySelection$,
149
+ axesSelection$,
150
+ defsSelection$,
151
+ graphicGSelection$
152
+ }
153
+ }
154
+
155
+
156
+ export const multiValueXYPositionObservable = ({ rootSelection, fullDataFormatter$, filteredMinMaxXYData$, multiValueContainerPosition$, layout$ }: {
157
+ rootSelection: d3.Selection<any, unknown, any, unknown>
158
+ fullDataFormatter$: Observable<DataFormatterMultiValue>
159
+ // computedData$: Observable<ComputedDataMultiValue>
160
+ // minMaxXY$: Observable<{ minX: number, maxX: number, minY: number, maxY: number }>
161
+ filteredMinMaxXYData$: Observable<{
162
+ minXDatum: ComputedLayoutDatumMultiValue
163
+ maxXDatum: ComputedLayoutDatumMultiValue
164
+ minYDatum: ComputedLayoutDatumMultiValue
165
+ maxYDatum: ComputedLayoutDatumMultiValue
166
+ }>
167
+ multiValueContainerPosition$: Observable<ContainerPositionScaled[]>
168
+ layout$: Observable<Layout>
169
+ }) => {
170
+ const rootMousemove$ = d3EventObservable(rootSelection, 'mousemove').pipe(
171
+ debounceTime(2) // 避免過度頻繁觸發,實測時沒加電腦容易卡頓
172
+ )
173
+
174
+ const columnAmount$ = multiValueContainerPosition$.pipe(
175
+ map(multiValueContainerPosition => {
176
+ const maxColumnIndex = multiValueContainerPosition.reduce((acc, current) => {
177
+ return current.columnIndex > acc ? current.columnIndex : acc
178
+ }, 0)
179
+ return maxColumnIndex + 1
180
+ }),
181
+ distinctUntilChanged(),
182
+ shareReplay(1)
183
+ )
184
+
185
+ const rowAmount$ = multiValueContainerPosition$.pipe(
186
+ map(multiValueContainerPosition => {
187
+ const maxRowIndex = multiValueContainerPosition.reduce((acc, current) => {
188
+ return current.rowIndex > acc ? current.rowIndex : acc
189
+ }, 0)
190
+ return maxRowIndex + 1
191
+ }),
192
+ distinctUntilChanged(),
193
+ shareReplay(1)
194
+ )
195
+
196
+ // const xyScale$ = combineLatest({
197
+ // layout: layout$,
198
+ // filteredMinMaxXYData: filteredMinMaxXYData$,
199
+ // fullDataFormatter: fullDataFormatter$,
200
+ // columnAmount: columnAmount$,
201
+ // rowAmount: rowAmount$
202
+ // }).pipe(
203
+ // switchMap(async d => d),
204
+ // map(data => {
205
+ // const xScale = createAxisToValueScale({
206
+ // maxValue: data.filteredMinMaxXYData.maxXDatum.value[0],
207
+ // minValue: data.filteredMinMaxXYData.minXDatum.value[0],
208
+ // axisWidth: data.layout.width,
209
+ // scaleDomain: data.fullDataFormatter.xAxis.scaleDomain,
210
+ // scaleRange: data.fullDataFormatter.xAxis.scaleRange,
211
+ // })
212
+ // const yScale = createAxisToValueScale({
213
+ // maxValue: data.filteredMinMaxXYData.maxYDatum.value[1],
214
+ // minValue: data.filteredMinMaxXYData.minYDatum.value[1],
215
+ // axisWidth: data.layout.height,
216
+ // scaleDomain: data.fullDataFormatter.yAxis.scaleDomain,
217
+ // scaleRange: data.fullDataFormatter.yAxis.scaleRange,
218
+ // reverse: true
219
+ // })
220
+ // return { xScale, yScale }
221
+ // })
222
+ // )
223
+
224
+ const xyScale$: Observable<{
225
+ xScale: d3.ScaleLinear<number, number, never>;
226
+ yScale: d3.ScaleLinear<number, number, never>;
227
+ }> = new Observable(subscriber => {
228
+ combineLatest({
229
+ layout: layout$,
230
+ filteredMinMaxXYData: filteredMinMaxXYData$,
231
+ fullDataFormatter: fullDataFormatter$,
232
+ columnAmount: columnAmount$,
233
+ rowAmount: rowAmount$
234
+ }).pipe(
235
+ switchMap(async d => d),
236
+ ).subscribe(data => {
237
+ if (!data.filteredMinMaxXYData.minXDatum || !data.filteredMinMaxXYData.maxXDatum
238
+ || data.filteredMinMaxXYData.minXDatum.value[0] == null || data.filteredMinMaxXYData.maxXDatum.value[0] == null
239
+ || !data.filteredMinMaxXYData.minYDatum || !data.filteredMinMaxXYData.maxYDatum
240
+ || data.filteredMinMaxXYData.minYDatum.value[1] == null || data.filteredMinMaxXYData.maxYDatum.value[1] == null
241
+ ) {
242
+ return
243
+ }
244
+ const xScale = createAxisToValueScale({
245
+ maxValue: data.filteredMinMaxXYData.maxXDatum.value[0],
246
+ minValue: data.filteredMinMaxXYData.minXDatum.value[0],
247
+ axisWidth: data.layout.width,
248
+ scaleDomain: data.fullDataFormatter.xAxis.scaleDomain,
249
+ scaleRange: data.fullDataFormatter.xAxis.scaleRange,
250
+ })
251
+ const yScale = createAxisToValueScale({
252
+ maxValue: data.filteredMinMaxXYData.maxYDatum.value[1],
253
+ minValue: data.filteredMinMaxXYData.minYDatum.value[1],
254
+ axisWidth: data.layout.height,
255
+ scaleDomain: data.fullDataFormatter.yAxis.scaleDomain,
256
+ scaleRange: data.fullDataFormatter.yAxis.scaleRange,
257
+ reverse: true
258
+ })
259
+ subscriber.next({ xScale, yScale })
260
+ })
261
+ })
262
+
263
+ const axisValue$ = combineLatest({
264
+ fullDataFormatter: fullDataFormatter$,
265
+ rootMousemove: rootMousemove$,
266
+ columnAmount: columnAmount$,
267
+ rowAmount: rowAmount$,
268
+ layout: layout$,
269
+ multiValueContainerPosition: multiValueContainerPosition$
270
+ }).pipe(
271
+ switchMap(async d => d),
272
+ map(data => {
273
+ // 由於event座標是基於底層的,但是container會有多欄,所以要重新計算
274
+ return {
275
+ x: ((data.rootMousemove.offsetX - data.layout.left) / data.multiValueContainerPosition[0].scale[0])
276
+ % (data.layout.rootWidth / data.columnAmount / data.multiValueContainerPosition[0].scale[0]),
277
+ y: ((data.rootMousemove.offsetY - data.layout.top) / data.multiValueContainerPosition[0].scale[1])
278
+ % (data.layout.rootHeight / data.rowAmount / data.multiValueContainerPosition[0].scale[1])
279
+ }
280
+ })
281
+ )
282
+
283
+ return combineLatest({
284
+ xyScale: xyScale$,
285
+ axisValue: axisValue$
286
+ }).pipe(
287
+ switchMap(async d => d),
288
+ map(data => {
289
+ return {
290
+ x: data.axisValue.x,
291
+ y: data.axisValue.y,
292
+ xValue: data.xyScale.xScale(data.axisValue.x),
293
+ yValue: data.xyScale.yScale(data.axisValue.y)
294
+ }
295
+ })
296
+ )
297
+ }