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

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