@orbcharts/plugins-basic 3.0.0-beta.7 → 3.0.0-beta.8

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 (126) hide show
  1. package/LICENSE +200 -200
  2. package/dist/orbcharts-plugins-basic/src/base/{BaseBarStack.d.ts → BaseStackedBar.d.ts} +4 -4
  3. package/dist/orbcharts-plugins-basic/src/grid/defaults.d.ts +3 -3
  4. package/dist/orbcharts-plugins-basic/src/grid/index.d.ts +2 -2
  5. package/dist/orbcharts-plugins-basic/src/grid/plugins/StackedBar.d.ts +1 -0
  6. package/dist/orbcharts-plugins-basic/src/grid/plugins/StackedValueAxis.d.ts +1 -0
  7. package/dist/orbcharts-plugins-basic/src/index.d.ts +1 -0
  8. package/dist/orbcharts-plugins-basic/src/multiGrid/defaults.d.ts +4 -4
  9. package/dist/orbcharts-plugins-basic/src/multiGrid/index.d.ts +3 -3
  10. package/dist/orbcharts-plugins-basic/src/multiGrid/plugins/MultiStackedBar.d.ts +1 -0
  11. package/dist/orbcharts-plugins-basic/src/multiGrid/plugins/MultiStackedValueAxis.d.ts +1 -0
  12. package/dist/orbcharts-plugins-basic/src/multiGrid/plugins/OverlappingStackedValueAxes.d.ts +1 -0
  13. package/dist/orbcharts-plugins-basic/src/noneData/plugins/Container.d.ts +0 -1
  14. package/dist/orbcharts-plugins-basic/src/noneData/plugins/Tooltip.d.ts +0 -3
  15. package/dist/orbcharts-plugins-basic/src/relationship/defaults.d.ts +5 -0
  16. package/dist/orbcharts-plugins-basic/src/relationship/index.d.ts +4 -0
  17. package/dist/orbcharts-plugins-basic/src/relationship/plugins/ForceDirected.d.ts +3 -0
  18. package/dist/orbcharts-plugins-basic/src/relationship/plugins/RelationshipLegend.d.ts +1 -0
  19. package/dist/orbcharts-plugins-basic/src/relationship/plugins/RelationshipTooltip.d.ts +1 -0
  20. package/dist/orbcharts-plugins-basic/src/relationship/relationshipObservables.d.ts +1 -0
  21. package/dist/orbcharts-plugins-basic/src/utils/commonUtils.d.ts +1 -1
  22. package/dist/orbcharts-plugins-basic/src/utils/orbchartsUtils.d.ts +1 -1
  23. package/dist/orbcharts-plugins-basic.es.js +8122 -7467
  24. package/dist/orbcharts-plugins-basic.umd.js +45 -32
  25. package/lib/core-types.ts +7 -7
  26. package/lib/core.ts +6 -6
  27. package/lib/plugins-basic-types.ts +6 -6
  28. package/package.json +44 -44
  29. package/src/base/BaseBars.ts +765 -765
  30. package/src/base/BaseBarsTriangle.ts +676 -676
  31. package/src/base/BaseDots.ts +464 -464
  32. package/src/base/BaseGroupAxis.ts +679 -679
  33. package/src/base/BaseLegend.ts +684 -684
  34. package/src/base/BaseLineAreas.ts +629 -629
  35. package/src/base/BaseLines.ts +706 -706
  36. package/src/base/{BaseBarStack.ts → BaseStackedBar.ts} +782 -782
  37. package/src/base/BaseTooltip.ts +385 -385
  38. package/src/base/BaseValueAxis.ts +583 -583
  39. package/src/base/types.ts +2 -2
  40. package/src/const.ts +30 -30
  41. package/src/grid/defaults.ts +246 -246
  42. package/src/grid/gridObservables.ts +554 -554
  43. package/src/grid/index.ts +16 -16
  44. package/src/grid/plugins/Bars.ts +69 -69
  45. package/src/grid/plugins/BarsPN.ts +66 -66
  46. package/src/grid/plugins/BarsTriangle.ts +73 -73
  47. package/src/grid/plugins/Dots.ts +68 -68
  48. package/src/grid/plugins/GridLegend.ts +107 -107
  49. package/src/grid/plugins/GridTooltip.ts +66 -66
  50. package/src/grid/plugins/GridZoom.ts +218 -218
  51. package/src/grid/plugins/GroupAux.ts +1103 -1103
  52. package/src/grid/plugins/GroupAxis.ts +97 -97
  53. package/src/grid/plugins/LineAreas.ts +65 -65
  54. package/src/grid/plugins/Lines.ts +59 -59
  55. package/src/grid/plugins/{BarStack.ts → StackedBar.ts} +64 -64
  56. package/src/grid/plugins/{ValueStackAxis.ts → StackedValueAxis.ts} +96 -96
  57. package/src/grid/plugins/ValueAxis.ts +94 -94
  58. package/src/index.ts +6 -10
  59. package/src/multiGrid/defaults.ts +224 -224
  60. package/src/multiGrid/index.ts +15 -15
  61. package/src/multiGrid/multiGridObservables.ts +49 -49
  62. package/src/multiGrid/plugins/MultiBars.ts +108 -108
  63. package/src/multiGrid/plugins/MultiBarsTriangle.ts +114 -114
  64. package/src/multiGrid/plugins/MultiDots.ts +102 -102
  65. package/src/multiGrid/plugins/MultiGridLegend.ts +159 -159
  66. package/src/multiGrid/plugins/MultiGridTooltip.ts +66 -66
  67. package/src/multiGrid/plugins/MultiGroupAxis.ts +137 -137
  68. package/src/multiGrid/plugins/MultiLineAreas.ts +107 -107
  69. package/src/multiGrid/plugins/MultiLines.ts +101 -101
  70. package/src/multiGrid/plugins/{MultiBarStack.ts → MultiStackedBar.ts} +106 -106
  71. package/src/multiGrid/plugins/{MultiValueStackAxis.ts → MultiStackedValueAxis.ts} +134 -134
  72. package/src/multiGrid/plugins/MultiValueAxis.ts +134 -134
  73. package/src/multiGrid/plugins/{OverlappingValueStackAxes.ts → OverlappingStackedValueAxes.ts} +299 -299
  74. package/src/multiGrid/plugins/OverlappingValueAxes.ts +300 -300
  75. package/src/multiValue/defaults.ts +166 -166
  76. package/src/multiValue/index.ts +8 -8
  77. package/src/multiValue/multiValueObservables.ts +297 -297
  78. package/src/multiValue/plugins/MultiValueLegend.ts +107 -107
  79. package/src/multiValue/plugins/MultiValueTooltip.ts +66 -66
  80. package/src/multiValue/plugins/Scatter.ts +426 -426
  81. package/src/multiValue/plugins/ScatterBubbles.ts +554 -554
  82. package/src/multiValue/plugins/XYAux.ts +681 -681
  83. package/src/multiValue/plugins/XYAxes.ts +684 -684
  84. package/src/multiValue/plugins/XYZoom.ts +299 -299
  85. package/src/noneData/defaults.ts +102 -102
  86. package/src/noneData/index.ts +3 -3
  87. package/src/noneData/plugins/Container.ts +28 -28
  88. package/src/noneData/plugins/Tooltip.ts +374 -374
  89. package/src/relationship/defaults.ts +113 -0
  90. package/src/relationship/index.ts +4 -0
  91. package/src/relationship/plugins/ForceDirected.ts +1148 -0
  92. package/src/relationship/plugins/RelationshipLegend.ts +100 -0
  93. package/src/relationship/plugins/RelationshipTooltip.ts +66 -0
  94. package/src/relationship/relationshipObservables.ts +50 -0
  95. package/src/series/defaults.ts +206 -206
  96. package/src/series/index.ts +9 -9
  97. package/src/series/plugins/Bubbles.ts +604 -603
  98. package/src/series/plugins/Pie.ts +623 -623
  99. package/src/series/plugins/PieEventTexts.ts +284 -283
  100. package/src/series/plugins/PieLabels.ts +640 -640
  101. package/src/series/plugins/Rose.ts +516 -516
  102. package/src/series/plugins/RoseLabels.ts +600 -600
  103. package/src/series/plugins/SeriesLegend.ts +107 -107
  104. package/src/series/plugins/SeriesTooltip.ts +66 -66
  105. package/src/series/seriesObservables.ts +145 -145
  106. package/src/series/seriesUtils.ts +51 -51
  107. package/src/tree/defaults.ts +78 -78
  108. package/src/tree/index.ts +4 -4
  109. package/src/tree/plugins/TreeLegend.ts +100 -100
  110. package/src/tree/plugins/TreeMap.ts +333 -333
  111. package/src/tree/plugins/TreeTooltip.ts +66 -66
  112. package/src/utils/commonUtils.ts +21 -21
  113. package/src/utils/d3Graphics.ts +174 -174
  114. package/src/utils/d3Utils.ts +74 -74
  115. package/src/utils/observables.ts +14 -14
  116. package/src/utils/orbchartsUtils.ts +115 -115
  117. package/tsconfig.base.json +13 -13
  118. package/tsconfig.json +2 -2
  119. package/vite.config.js +22 -22
  120. package/dist/orbcharts-plugins-basic/src/grid/plugins/BarStack.d.ts +0 -1
  121. package/dist/orbcharts-plugins-basic/src/grid/plugins/ValueStackAxis.d.ts +0 -1
  122. package/dist/orbcharts-plugins-basic/src/multiGrid/plugins/MultiBarStack.d.ts +0 -1
  123. package/dist/orbcharts-plugins-basic/src/multiGrid/plugins/MultiValueStackAxis.d.ts +0 -1
  124. package/dist/orbcharts-plugins-basic/src/multiGrid/plugins/OverlappingValueStackAxes.d.ts +0 -1
  125. /package/dist/orbcharts-plugins-basic/src/relationship/plugins/{Relationship.d.ts → ForceDirectedBubbles.d.ts} +0 -0
  126. /package/src/relationship/plugins/{Relationship.ts → ForceDirectedBubbles.ts} +0 -0
@@ -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
+ }