@orbcharts/plugins-basic 3.0.0-alpha.44 → 3.0.0-alpha.45

Sign up to get free protection for your applications and to get access to all the features.
Files changed (155) hide show
  1. package/LICENSE +200 -200
  2. package/dist/{src → orbcharts-plugins-basic/src}/base/BaseBarStack.d.ts +6 -4
  3. package/dist/{src → orbcharts-plugins-basic/src}/base/BaseBars.d.ts +6 -4
  4. package/dist/{src → orbcharts-plugins-basic/src}/base/BaseBarsTriangle.d.ts +7 -4
  5. package/dist/{src → orbcharts-plugins-basic/src}/base/BaseDots.d.ts +5 -3
  6. package/dist/{src → orbcharts-plugins-basic/src}/base/BaseGroupAxis.d.ts +3 -3
  7. package/dist/{src → orbcharts-plugins-basic/src}/base/BaseLineAreas.d.ts +6 -3
  8. package/dist/{src → orbcharts-plugins-basic/src}/base/BaseLines.d.ts +6 -3
  9. package/dist/{src → orbcharts-plugins-basic/src}/base/BaseValueAxis.d.ts +3 -3
  10. package/dist/{src → orbcharts-plugins-basic/src}/grid/gridObservables.d.ts +4 -4
  11. package/dist/orbcharts-plugins-basic/src/index.d.ts +5 -0
  12. package/dist/orbcharts-plugins-basic/src/multiGrid/multiGridObservables.d.ts +8 -0
  13. package/dist/orbcharts-plugins-basic/src/series/plugins/PieEventTexts.d.ts +3 -0
  14. package/dist/orbcharts-plugins-basic/src/series/seriesObservables.d.ts +21 -0
  15. package/dist/{src → orbcharts-plugins-basic/src}/series/seriesUtils.d.ts +3 -3
  16. package/dist/orbcharts-plugins-basic.es.js +7696 -7529
  17. package/dist/orbcharts-plugins-basic.umd.js +8 -8
  18. package/dist/src/index.d.ts +1 -5
  19. package/package.json +42 -42
  20. package/src/base/BaseBarStack.ts +778 -881
  21. package/src/base/BaseBars.ts +764 -750
  22. package/src/base/BaseBarsTriangle.ts +672 -659
  23. package/src/base/BaseDots.ts +502 -639
  24. package/src/base/BaseGroupAxis.ts +496 -496
  25. package/src/base/BaseLegend.ts +641 -641
  26. package/src/base/BaseLineAreas.ts +625 -621
  27. package/src/base/BaseLines.ts +699 -692
  28. package/src/base/BaseValueAxis.ts +478 -479
  29. package/src/base/types.ts +2 -2
  30. package/src/grid/defaults.ts +121 -121
  31. package/src/grid/gridObservables.ts +247 -263
  32. package/src/grid/index.ts +15 -15
  33. package/src/grid/plugins/BarStack.ts +50 -37
  34. package/src/grid/plugins/Bars.ts +51 -37
  35. package/src/grid/plugins/BarsDiverging.ts +41 -39
  36. package/src/grid/plugins/BarsTriangle.ts +50 -34
  37. package/src/grid/plugins/Dots.ts +37 -35
  38. package/src/grid/plugins/GridLegend.ts +59 -59
  39. package/src/grid/plugins/GroupAux.ts +645 -646
  40. package/src/grid/plugins/GroupAxis.ts +42 -30
  41. package/src/grid/plugins/LineAreas.ts +39 -36
  42. package/src/grid/plugins/Lines.ts +38 -35
  43. package/src/grid/plugins/ScalingArea.ts +173 -174
  44. package/src/grid/plugins/ValueAxis.ts +43 -31
  45. package/src/grid/plugins/ValueStackAxis.ts +79 -70
  46. package/src/grid/types.ts +120 -120
  47. package/src/index.ts +9 -9
  48. package/src/multiGrid/defaults.ts +147 -147
  49. package/src/multiGrid/index.ts +11 -11
  50. package/src/multiGrid/multiGridObservables.ts +42 -289
  51. package/src/multiGrid/plugins/MultiBarStack.ts +74 -60
  52. package/src/multiGrid/plugins/MultiBars.ts +73 -59
  53. package/src/multiGrid/plugins/MultiBarsTriangle.ts +73 -58
  54. package/src/multiGrid/plugins/MultiDots.ts +60 -58
  55. package/src/multiGrid/plugins/MultiGridLegend.ts +89 -89
  56. package/src/multiGrid/plugins/MultiGroupAxis.ts +65 -53
  57. package/src/multiGrid/plugins/MultiLineAreas.ts +62 -59
  58. package/src/multiGrid/plugins/MultiLines.ts +61 -58
  59. package/src/multiGrid/plugins/MultiValueAxis.ts +65 -53
  60. package/src/multiGrid/plugins/OverlappingValueAxes.ts +169 -164
  61. package/src/multiGrid/types.ts +67 -67
  62. package/src/noneData/defaults.ts +64 -61
  63. package/src/noneData/index.ts +3 -3
  64. package/src/noneData/plugins/Container.ts +10 -10
  65. package/src/noneData/plugins/Tooltip.ts +310 -310
  66. package/src/noneData/types.ts +26 -26
  67. package/src/series/defaults.ts +109 -99
  68. package/src/series/index.ts +6 -6
  69. package/src/series/plugins/Bubbles.ts +571 -551
  70. package/src/series/plugins/Pie.ts +548 -600
  71. package/src/series/plugins/PieEventTexts.ts +258 -194
  72. package/src/series/plugins/PieLabels.ts +335 -288
  73. package/src/series/plugins/SeriesLegend.ts +59 -59
  74. package/src/series/seriesObservables.ts +145 -0
  75. package/src/series/seriesUtils.ts +50 -50
  76. package/src/series/types.ts +67 -67
  77. package/src/tree/defaults.ts +22 -22
  78. package/src/tree/index.ts +3 -3
  79. package/src/tree/plugins/TreeLegend.ts +59 -59
  80. package/src/tree/plugins/TreeMap.ts +305 -305
  81. package/src/tree/types.ts +23 -23
  82. package/src/utils/commonUtils.ts +21 -21
  83. package/src/utils/d3Graphics.ts +124 -124
  84. package/src/utils/d3Utils.ts +73 -73
  85. package/src/utils/observables.ts +14 -14
  86. package/src/utils/orbchartsUtils.ts +100 -100
  87. package/tsconfig.dev.json +16 -16
  88. package/tsconfig.json +16 -13
  89. package/tsconfig.prod.json +13 -13
  90. package/vite.config.js +49 -49
  91. package/dist/src/multiGrid/multiGridObservables.d.ts +0 -12
  92. package/dist/src/series/plugins/PieEventTexts.d.ts +0 -1
  93. /package/dist/{src → orbcharts-plugins-basic/src}/base/BaseGroupArea.d.ts +0 -0
  94. /package/dist/{src → orbcharts-plugins-basic/src}/base/BaseLegend.d.ts +0 -0
  95. /package/dist/{src → orbcharts-plugins-basic/src}/base/types.d.ts +0 -0
  96. /package/dist/{src → orbcharts-plugins-basic/src}/grid/defaults.d.ts +0 -0
  97. /package/dist/{src → orbcharts-plugins-basic/src}/grid/index.d.ts +0 -0
  98. /package/dist/{src → orbcharts-plugins-basic/src}/grid/plugins/BarStack.d.ts +0 -0
  99. /package/dist/{src → orbcharts-plugins-basic/src}/grid/plugins/Bars.d.ts +0 -0
  100. /package/dist/{src → orbcharts-plugins-basic/src}/grid/plugins/BarsDiverging.d.ts +0 -0
  101. /package/dist/{src → orbcharts-plugins-basic/src}/grid/plugins/BarsTriangle.d.ts +0 -0
  102. /package/dist/{src → orbcharts-plugins-basic/src}/grid/plugins/Dots.d.ts +0 -0
  103. /package/dist/{src → orbcharts-plugins-basic/src}/grid/plugins/GridLegend.d.ts +0 -0
  104. /package/dist/{src → orbcharts-plugins-basic/src}/grid/plugins/GroupAux.d.ts +0 -0
  105. /package/dist/{src → orbcharts-plugins-basic/src}/grid/plugins/GroupAxis.d.ts +0 -0
  106. /package/dist/{src → orbcharts-plugins-basic/src}/grid/plugins/LineAreas.d.ts +0 -0
  107. /package/dist/{src → orbcharts-plugins-basic/src}/grid/plugins/Lines.d.ts +0 -0
  108. /package/dist/{src → orbcharts-plugins-basic/src}/grid/plugins/Ranking.d.ts +0 -0
  109. /package/dist/{src → orbcharts-plugins-basic/src}/grid/plugins/RankingAxis.d.ts +0 -0
  110. /package/dist/{src → orbcharts-plugins-basic/src}/grid/plugins/ScalingArea.d.ts +0 -0
  111. /package/dist/{src → orbcharts-plugins-basic/src}/grid/plugins/ValueAxis.d.ts +0 -0
  112. /package/dist/{src → orbcharts-plugins-basic/src}/grid/plugins/ValueStackAxis.d.ts +0 -0
  113. /package/dist/{src → orbcharts-plugins-basic/src}/grid/types.d.ts +0 -0
  114. /package/dist/{src → orbcharts-plugins-basic/src}/multiGrid/defaults.d.ts +0 -0
  115. /package/dist/{src → orbcharts-plugins-basic/src}/multiGrid/index.d.ts +0 -0
  116. /package/dist/{src → orbcharts-plugins-basic/src}/multiGrid/plugins/MultiBarStack.d.ts +0 -0
  117. /package/dist/{src → orbcharts-plugins-basic/src}/multiGrid/plugins/MultiBars.d.ts +0 -0
  118. /package/dist/{src → orbcharts-plugins-basic/src}/multiGrid/plugins/MultiBarsTriangle.d.ts +0 -0
  119. /package/dist/{src → orbcharts-plugins-basic/src}/multiGrid/plugins/MultiDots.d.ts +0 -0
  120. /package/dist/{src → orbcharts-plugins-basic/src}/multiGrid/plugins/MultiGridLegend.d.ts +0 -0
  121. /package/dist/{src → orbcharts-plugins-basic/src}/multiGrid/plugins/MultiGroupAxis.d.ts +0 -0
  122. /package/dist/{src → orbcharts-plugins-basic/src}/multiGrid/plugins/MultiLineAreas.d.ts +0 -0
  123. /package/dist/{src → orbcharts-plugins-basic/src}/multiGrid/plugins/MultiLines.d.ts +0 -0
  124. /package/dist/{src → orbcharts-plugins-basic/src}/multiGrid/plugins/MultiValueAxis.d.ts +0 -0
  125. /package/dist/{src → orbcharts-plugins-basic/src}/multiGrid/plugins/OverlappingValueAxes.d.ts +0 -0
  126. /package/dist/{src → orbcharts-plugins-basic/src}/multiGrid/types.d.ts +0 -0
  127. /package/dist/{src → orbcharts-plugins-basic/src}/multiValue/index.d.ts +0 -0
  128. /package/dist/{src → orbcharts-plugins-basic/src}/multiValue/plugins/Scatter.d.ts +0 -0
  129. /package/dist/{src → orbcharts-plugins-basic/src}/multiValue/plugins/ScatterAxes.d.ts +0 -0
  130. /package/dist/{src → orbcharts-plugins-basic/src}/noneData/defaults.d.ts +0 -0
  131. /package/dist/{src → orbcharts-plugins-basic/src}/noneData/index.d.ts +0 -0
  132. /package/dist/{src → orbcharts-plugins-basic/src}/noneData/plugins/Container.d.ts +0 -0
  133. /package/dist/{src → orbcharts-plugins-basic/src}/noneData/plugins/Tooltip.d.ts +0 -0
  134. /package/dist/{src → orbcharts-plugins-basic/src}/noneData/types.d.ts +0 -0
  135. /package/dist/{src → orbcharts-plugins-basic/src}/relationship/index.d.ts +0 -0
  136. /package/dist/{src → orbcharts-plugins-basic/src}/relationship/plugins/Relationship.d.ts +0 -0
  137. /package/dist/{src → orbcharts-plugins-basic/src}/series/defaults.d.ts +0 -0
  138. /package/dist/{src → orbcharts-plugins-basic/src}/series/index.d.ts +0 -0
  139. /package/dist/{src → orbcharts-plugins-basic/src}/series/plugins/Bubbles.d.ts +0 -0
  140. /package/dist/{src → orbcharts-plugins-basic/src}/series/plugins/Pie.d.ts +0 -0
  141. /package/dist/{src → orbcharts-plugins-basic/src}/series/plugins/PieLabels.d.ts +0 -0
  142. /package/dist/{src → orbcharts-plugins-basic/src}/series/plugins/SeriesLegend.d.ts +0 -0
  143. /package/dist/{src → orbcharts-plugins-basic/src}/series/plugins/Waffle.d.ts +0 -0
  144. /package/dist/{src → orbcharts-plugins-basic/src}/series/types.d.ts +0 -0
  145. /package/dist/{src → orbcharts-plugins-basic/src}/tree/defaults.d.ts +0 -0
  146. /package/dist/{src → orbcharts-plugins-basic/src}/tree/index.d.ts +0 -0
  147. /package/dist/{src → orbcharts-plugins-basic/src}/tree/plugins/TreeLegend.d.ts +0 -0
  148. /package/dist/{src → orbcharts-plugins-basic/src}/tree/plugins/TreeMap.d.ts +0 -0
  149. /package/dist/{src → orbcharts-plugins-basic/src}/tree/types.d.ts +0 -0
  150. /package/dist/{src → orbcharts-plugins-basic/src}/utils/commonUtils.d.ts +0 -0
  151. /package/dist/{src → orbcharts-plugins-basic/src}/utils/d3Graphics.d.ts +0 -0
  152. /package/dist/{src → orbcharts-plugins-basic/src}/utils/d3Utils.d.ts +0 -0
  153. /package/dist/{src → orbcharts-plugins-basic/src}/utils/observables.d.ts +0 -0
  154. /package/dist/{src → orbcharts-plugins-basic/src}/utils/orbchartsUtils.d.ts +0 -0
  155. /package/dist/{vite.config.d.ts → orbcharts-plugins-basic/vite.config.d.ts} +0 -0
@@ -1,552 +1,572 @@
1
- import * as d3 from 'd3'
2
- import {
3
- combineLatest,
4
- map,
5
- switchMap,
6
- first,
7
- takeUntil,
8
- Subject,
9
- Observable,
10
- distinctUntilChanged} from 'rxjs'
11
- import type {
12
- ChartParams,
13
- DatumValue,
14
- DataSeries,
15
- EventName,
16
- ComputedDataSeries,
17
- ComputedDatumSeries } from '@orbcharts/core'
18
- import {
19
- defineSeriesPlugin } from '@orbcharts/core'
20
- import type { BubblesParams, BubbleScaleType } from '../types'
21
- import { DEFAULT_BUBBLES_PARAMS } from '../defaults'
22
- import { renderCircleText } from '../../utils/d3Graphics'
23
-
24
- interface BubblesDatum extends ComputedDatumSeries {
25
- x: number
26
- y: number
27
- r: number
28
- _originR: number // 紀錄變化前的r
29
- }
30
-
31
- let force: d3.Simulation<d3.SimulationNodeDatum, undefined> | undefined
32
-
33
- function makeForce (bubblesSelection: d3.Selection<SVGGElement, any, any, any>, fullParams: BubblesParams) {
34
- return d3.forceSimulation()
35
- .velocityDecay(fullParams.force!.velocityDecay!)
36
- // .alphaDecay(0.2)
37
- .force(
38
- "collision",
39
- d3.forceCollide()
40
- .radius(d => {
41
- // @ts-ignore
42
- return d.r + fullParams.force!.collisionSpacing
43
- })
44
- // .strength(0.01)
45
- )
46
- .force("charge", d3.forceManyBody().strength((d) => {
47
- // @ts-ignore
48
- return - Math.pow(d.r, 2.0) * fullParams.force!.strength
49
- }))
50
- // .force("x", d3.forceX().strength(forceStrength).x(this.graphicWidth / 2))
51
- // .force("y", d3.forceY().strength(forceStrength).y(this.graphicHeight / 2))
52
- .on("tick", () => {
53
- // if (!bubblesSelection) {
54
- // return
55
- // }
56
- bubblesSelection
57
- .attr("transform", (d) => {
58
- return `translate(${d.x},${d.y})`
59
- })
60
- // .attr("cx", (d) => d.x)
61
- // .attr("cy", (d) => d.y)
62
- })
63
- }
64
-
65
-
66
- // 計算最大泡泡的半徑
67
- function getMaxR ({ data, bubbleGroupR, maxValue, avgValue }: {
68
- data: DatumValue[]
69
- bubbleGroupR: number
70
- maxValue: number
71
- avgValue: number
72
- }) {
73
- // 平均r(假想是正方型來計算的,比如說大正方型裡有4個正方型,則 r = width/Math.sqrt(4)/2)
74
- const avgR = bubbleGroupR / Math.sqrt(data.length)
75
- const avgSize = avgR * avgR * Math.PI
76
- const sizeRate = avgSize / avgValue
77
- const maxSize = maxValue * sizeRate
78
- const maxR = Math.pow(maxSize / Math.PI, 0.5)
79
-
80
- const modifier = 0.75 // @Q@ 因為以下公式是假設泡泡是正方型來計算,所以畫出來的圖會偏大一些,這個數值是用來修正用的
81
- return maxR * modifier
82
- }
83
-
84
- function createBubblesData ({ data, LastBubbleDataMap, graphicWidth, graphicHeight, scaleType }: {
85
- data: ComputedDataSeries
86
- LastBubbleDataMap: Map<string, BubblesDatum>
87
- graphicWidth: number
88
- graphicHeight: number
89
- scaleType: BubbleScaleType
90
- // highlightIds: string[]
91
- }) {
92
- const bubbleGroupR = Math.min(...[graphicWidth, graphicHeight]) / 2
93
-
94
- const filteredData = data
95
- .flat()
96
- .filter(_d => _d.value != null && _d.visible != false)
97
-
98
- const maxValue = Math.max(
99
- ...filteredData.map(_d => _d.value!)
100
- )
101
-
102
- const avgValue = (
103
- filteredData.reduce((prev, current) => prev + (current.value ?? 0), 0)
104
- ) / filteredData.length
105
-
106
- const maxR = getMaxR({ data: filteredData, bubbleGroupR, maxValue, avgValue })
107
-
108
- const exponent = scaleType === 'area'
109
- ? 0.5 // 比例映射面積(0.5為取平方根)
110
- : 1 // 比例映射半徑
111
-
112
- const scaleBubbleR = d3.scalePow()
113
- .domain([0, maxValue])
114
- .range([0, maxR])
115
- .exponent(exponent)
116
-
117
- const bubbleData: BubblesDatum[] = filteredData.map((_d) => {
118
- const d: BubblesDatum = _d as BubblesDatum
119
-
120
- const existDatum = LastBubbleDataMap.get(_d.id)
121
-
122
- if (existDatum) {
123
- // 使用現有的座標
124
- d.x = existDatum.x
125
- d.y = existDatum.y
126
- } else {
127
- d.x = Math.random() * graphicWidth
128
- d.y = Math.random() * graphicHeight
129
- }
130
- const r = scaleBubbleR!(d.value ?? 0)!
131
- d.r = r
132
- d._originR = r
133
-
134
- return d
135
- })
136
-
137
- return bubbleData
138
- }
139
-
140
- function renderBubbles ({ graphicSelection, bubblesData, fullParams }: {
141
- graphicSelection: d3.Selection<SVGGElement, any, any, any>
142
- bubblesData: BubblesDatum[]
143
- fullParams: BubblesParams
144
- }) {
145
- let update = graphicSelection.selectAll<SVGGElement, BubblesDatum>("g")
146
- .data(bubblesData, (d) => d.id)
147
- let enter = update.enter()
148
- .append<SVGGElement>("g")
149
- .attr('cursor', 'pointer')
150
- enter
151
- .attr('font-size', 12)
152
- .style('fill', '#ffffff')
153
- .attr("text-anchor", "middle")
154
- .attr("transform", (d) => {
155
- return `translate(${d.x},${d.y})`
156
- })
157
- // .attr("cx", (d) => d.x)
158
- // .attr("cy", (d) => d.y)
159
-
160
- enter
161
- .append("circle")
162
- .attr("class", "node")
163
- // update.merge(enter)
164
- .attr("cx", 0)
165
- .attr("cy", 0)
166
- // .attr("r", 1e-6)
167
- .attr('fill', (d) => d.color)
168
- // .transition()
169
- // .duration(500)
170
-
171
- enter
172
- .append('text')
173
- .style('opacity', 0.8)
174
- .attr('pointer-events', 'none')
175
-
176
- update.exit().remove()
177
-
178
- const bubblesSelection = update.merge(enter)
179
-
180
- bubblesSelection.select('circle')
181
- .transition()
182
- .duration(200)
183
- .attr("r", (d) => d.r)
184
- .attr('fill', (d) => d.color)
185
- bubblesSelection
186
- .each((d,i,g) => {
187
- const gSelection = d3.select(g[i])
188
- let breakAll = true
189
- if (d.label.length <= fullParams.bubbleText.lineLengthMin) {
190
- breakAll = false
191
- }
192
- gSelection.call(renderCircleText, {
193
- text: d.label,
194
- radius: d.r * fullParams.bubbleText.fillRate,
195
- lineHeight: fullParams.bubbleText.lineHeight,
196
- isBreakAll: breakAll
197
- })
198
-
199
- })
200
-
201
- return bubblesSelection
202
- }
203
-
204
- function setHighlightData ({ data, highlightRIncrease, highlightIds }: {
205
- data: BubblesDatum[]
206
- // fullParams: BubblesParams
207
- highlightRIncrease: number
208
- highlightIds: string[]
209
- }) {
210
- if (highlightRIncrease == 0) {
211
- return
212
- }
213
- if (!highlightIds.length) {
214
- data.forEach(d => d.r = d._originR)
215
- return
216
- }
217
- data.forEach(d => {
218
- if (highlightIds.includes(d.id)) {
219
- d.r = d._originR + highlightRIncrease
220
- } else {
221
- d.r = d._originR
222
- }
223
- })
224
- }
225
-
226
- function drag (): d3.DragBehavior<Element, unknown, unknown> {
227
- return d3.drag()
228
- .on("start", (event, d: any) => {
229
- if (!event.active) {
230
- force!.alpha(1).restart();
231
- }
232
- d.fx = d.x
233
- d.fy = d.y
234
- })
235
- .on("drag", (event, d: any) => {
236
- if (!event.active) {
237
- force!.alphaTarget(0)
238
- }
239
- d.fx = event.x
240
- d.fy = event.y
241
- })
242
- .on("end", (event, d: any) => {
243
- d.fx = null
244
- d.fy = null
245
- })
246
- }
247
-
248
-
249
- // private nodeTypePos (d: any) {
250
- // console.log(d)
251
- // console.log(this.TypeCenters.get(d.type)!)
252
- // const typeCenter = this.TypeCenters.get(d.type)!
253
- // return typeCenter ? typeCenter.x : 0
254
- // }
255
-
256
- function groupBubbles ({ fullParams, graphicWidth, graphicHeight }: {
257
- fullParams: BubblesParams
258
- graphicWidth: number
259
- graphicHeight: number
260
- }) {
261
- force!
262
- // .force('x', d3.forceX().strength(fullParams.force.strength).x(graphicWidth / 2))
263
- // .force('y', d3.forceY().strength(fullParams.force.strength).y(graphicHeight / 2))
264
- .force('x', d3.forceX().strength(fullParams.force.strength).x(0))
265
- .force('y', d3.forceY().strength(fullParams.force.strength).y(0))
266
-
267
- force!.alpha(1).restart();
268
- }
269
-
270
- function highlight ({ bubblesSelection, highlightIds, fullChartParams }: {
271
- bubblesSelection: d3.Selection<SVGGElement, BubblesDatum, any, any>
272
- fullChartParams: ChartParams
273
- highlightIds: string[]
274
- }) {
275
- bubblesSelection.interrupt('highlight')
276
-
277
- if (!highlightIds.length) {
278
- bubblesSelection
279
- .transition('highlight')
280
- .style('opacity', 1)
281
- return
282
- }
283
-
284
- bubblesSelection.each((d, i, n) => {
285
- const segment = d3.select(n[i])
286
-
287
- if (highlightIds.includes(d.id)) {
288
- segment
289
- .style('opacity', 1)
290
- .transition('highlight')
291
- .ease(d3.easeElastic)
292
- .duration(500)
293
- } else {
294
- // 取消放大
295
- segment
296
- .style('opacity', fullChartParams.styles.unhighlightedOpacity)
297
- }
298
- })
299
- }
300
-
301
- export const Bubbles = defineSeriesPlugin('Bubbles', DEFAULT_BUBBLES_PARAMS)(({ selection, name, observer, subject }) => {
302
-
303
- const destroy$ = new Subject()
304
-
305
- const graphicSelection: d3.Selection<SVGGElement, any, any, any> = selection.append('g')
306
- const bubblesSelection$: Subject<d3.Selection<SVGGElement, BubblesDatum, any, any>> = new Subject()
307
- // 紀錄前一次bubble data
308
- let LastBubbleDataMap: Map<string, BubblesDatum> = new Map()
309
-
310
-
311
- // fullParams$.subscribe(d => {
312
- // force = makeForce(bubblesSelection, d)
313
- // })
314
-
315
- observer.layout$
316
- .pipe(
317
- first()
318
- )
319
- .subscribe(size => {
320
- selection
321
- .attr('transform', `translate(${size.width / 2}, ${size.height / 2})`)
322
- observer.layout$
323
- .pipe(
324
- takeUntil(destroy$)
325
- )
326
- .subscribe(size => {
327
- selection
328
- .transition()
329
- .attr('transform', `translate(${size.width / 2}, ${size.height / 2})`)
330
- })
331
- })
332
-
333
- // const bubbleGroupR$ = layout$.pipe(
334
- // map(d => {
335
- // const minWidth = Math.min(...[d.width, d.height])
336
- // return minWidth / 2
337
- // })
338
- // )
339
-
340
- // const maxValue$ = computedData$.pipe(
341
- // map(d => Math.max(
342
- // ...d
343
- // .flat()
344
- // .filter(_d => _d.value != null)
345
- // .map(_d => _d.value!)
346
- // )
347
- // )
348
- // )
349
-
350
- // const avgValue$ = computedData$.pipe(
351
- // map(d => {
352
- // const total = d
353
- // .flat()
354
- // .reduce((prev, current) => prev + (current.value ?? 0), 0)
355
- // return total / d.length
356
- // })
357
- // )
358
-
359
- // const SeriesDataMap$ = observer.computedData$.pipe(
360
- // takeUntil(destroy$),
361
- // map(d => makeSeriesDataMap(d))
362
- // )
363
-
364
- const scaleType$ = observer.fullParams$.pipe(
365
- takeUntil(destroy$),
366
- map(d => d.bubbleScaleType),
367
- distinctUntilChanged()
368
- )
369
-
370
- const bubblesData$ = new Observable<BubblesDatum[]>(subscriber => {
371
- combineLatest({
372
- layout: observer.layout$,
373
- computedData: observer.computedData$,
374
- scaleType: scaleType$
375
- }).pipe(
376
- takeUntil(destroy$),
377
- // 轉換後會退訂前一個未完成的訂閱事件,因此可以取到「同時間」最後一次的訂閱事件
378
- switchMap(async (d) => d),
379
- ).subscribe(data => {
380
- const bubblesData = createBubblesData({
381
- data: data.computedData,
382
- LastBubbleDataMap,
383
- graphicWidth: data.layout.width,
384
- graphicHeight: data.layout.height,
385
- scaleType: data.scaleType
386
- })
387
- subscriber.next(bubblesData)
388
- })
389
- })
390
-
391
- // 紀錄前一次bubble data
392
- bubblesData$.subscribe(d => {
393
- LastBubbleDataMap = new Map(d.map(_d => [_d.id, _d])) // key: id, value: datum
394
- })
395
-
396
- const highlightTarget$ = observer.fullChartParams$.pipe(
397
- takeUntil(destroy$),
398
- map(d => d.highlightTarget),
399
- distinctUntilChanged()
400
- )
401
-
402
- combineLatest({
403
- layout: observer.layout$,
404
- computedData: observer.computedData$,
405
- bubblesData: bubblesData$,
406
- SeriesDataMap: observer.SeriesDataMap$,
407
- fullParams: observer.fullParams$,
408
- highlightTarget: highlightTarget$
409
- // fullChartParams: fullChartParams$
410
- // highlight: highlight$
411
- }).pipe(
412
- takeUntil(destroy$),
413
- // 轉換後會退訂前一個未完成的訂閱事件,因此可以取到「同時間」最後一次的訂閱事件
414
- switchMap(async (d) => d),
415
- ).subscribe(data => {
416
-
417
- const bubblesSelection = renderBubbles({
418
- graphicSelection,
419
- bubblesData: data.bubblesData,
420
- fullParams: data.fullParams
421
- })
422
-
423
- force = makeForce(bubblesSelection, data.fullParams)
424
-
425
- bubblesSelection
426
- .on('mouseover', (event, datum) => {
427
- // this.tooltip!.setDatum({
428
- // data: d,
429
- // x: d3.event.clientX,
430
- // y: d3.event.clientY
431
- // })
432
-
433
- subject.event$.next({
434
- type: 'series',
435
- eventName: 'mouseover',
436
- pluginName: name,
437
- highlightTarget: data.highlightTarget,
438
- datum,
439
- series: data.SeriesDataMap.get(datum.seriesLabel)!,
440
- seriesIndex: datum.seriesIndex,
441
- seriesLabel: datum.seriesLabel,
442
- event,
443
- data: data.computedData
444
- })
445
- })
446
- .on('mousemove', (event, datum) => {
447
- // this.tooltip!.setDatum({
448
- // x: d3.event.clientX,
449
- // y: d3.event.clientY
450
- // })
451
-
452
- subject.event$.next({
453
- type: 'series',
454
- eventName: 'mousemove',
455
- pluginName: name,
456
- highlightTarget: data.highlightTarget,
457
- datum,
458
- series: data.SeriesDataMap.get(datum.seriesLabel)!,
459
- seriesIndex: datum.seriesIndex,
460
- seriesLabel: datum.seriesLabel,
461
- event,
462
- data: data.computedData
463
- })
464
- })
465
- .on('mouseout', (event, datum) => {
466
- // this.tooltip!.remove()
467
-
468
- subject.event$.next({
469
- type: 'series',
470
- eventName: 'mouseout',
471
- pluginName: name,
472
- highlightTarget: data.highlightTarget,
473
- datum,
474
- series: data.SeriesDataMap.get(datum.seriesLabel)!,
475
- seriesIndex: datum.seriesIndex,
476
- seriesLabel: datum.seriesLabel,
477
- event,
478
- data: data.computedData
479
- })
480
- })
481
- .on('click', (event, datum) => {
482
-
483
- subject.event$.next({
484
- type: 'series',
485
- eventName: 'click',
486
- pluginName: name,
487
- highlightTarget: data.highlightTarget,
488
- datum,
489
- series: data.SeriesDataMap.get(datum.seriesLabel)!,
490
- seriesIndex: datum.seriesIndex,
491
- seriesLabel: datum.seriesLabel,
492
- event,
493
- data: data.computedData
494
- })
495
- })
496
- .call(drag() as any)
497
-
498
- force!.nodes(data.bubblesData);
499
-
500
- groupBubbles({
501
- fullParams: data.fullParams,
502
- graphicWidth: data.layout.width,
503
- graphicHeight: data.layout.height
504
- })
505
-
506
- bubblesSelection$.next(bubblesSelection)
507
- })
508
-
509
- combineLatest({
510
- bubblesSelection: bubblesSelection$,
511
- bubblesData: bubblesData$,
512
- highlight: observer.seriesHighlight$.pipe(
513
- map(data => data.map(d => d.id))
514
- ),
515
- fullChartParams: observer.fullChartParams$,
516
- fullParams: observer.fullParams$,
517
- layout: observer.layout$
518
- }).pipe(
519
- takeUntil(destroy$),
520
- switchMap(async d => d)
521
- ).subscribe(data => {
522
- highlight({
523
- bubblesSelection: data.bubblesSelection,
524
- highlightIds: data.highlight,
525
- fullChartParams: data.fullChartParams
526
- })
527
-
528
- if (data.fullParams.highlightRIncrease) {
529
- setHighlightData ({
530
- data: data.bubblesData,
531
- highlightRIncrease: data.fullParams.highlightRIncrease,
532
- highlightIds: data.highlight
533
- })
534
- renderBubbles({
535
- graphicSelection,
536
- bubblesData: data.bubblesData,
537
- fullParams: data.fullParams
538
- })
539
- }
540
-
541
- groupBubbles({
542
- fullParams: data.fullParams,
543
- graphicWidth: data.layout.width,
544
- graphicHeight: data.layout.height
545
- })
546
- force!.nodes(data.bubblesData);
547
- })
548
-
549
- return () => {
550
- destroy$.next(undefined)
551
- }
1
+ import * as d3 from 'd3'
2
+ import {
3
+ combineLatest,
4
+ map,
5
+ switchMap,
6
+ first,
7
+ takeUntil,
8
+ Subject,
9
+ Observable,
10
+ distinctUntilChanged} from 'rxjs'
11
+ import type {
12
+ ChartParams,
13
+ DatumValue,
14
+ DataSeries,
15
+ EventName,
16
+ ComputedDataSeries,
17
+ ComputedDatumSeries,
18
+ SeriesContainerPosition } from '@orbcharts/core'
19
+ import {
20
+ defineSeriesPlugin } from '@orbcharts/core'
21
+ import type { BubblesParams, BubbleScaleType } from '../types'
22
+ import { DEFAULT_BUBBLES_PARAMS } from '../defaults'
23
+ import { renderCircleText } from '../../utils/d3Graphics'
24
+
25
+ interface BubblesDatum extends ComputedDatumSeries {
26
+ x: number
27
+ y: number
28
+ r: number
29
+ _originR: number // 紀錄變化前的r
30
+ }
31
+
32
+ type BubblesSimulationDatum = BubblesDatum & d3.SimulationNodeDatum
33
+
34
+ let force: d3.Simulation<d3.SimulationNodeDatum, undefined> | undefined
35
+
36
+ function makeForce (bubblesSelection: d3.Selection<SVGGElement, any, any, any>, fullParams: BubblesParams) {
37
+ return d3.forceSimulation()
38
+ .velocityDecay(fullParams.force!.velocityDecay!)
39
+ // .alphaDecay(0.2)
40
+ .force(
41
+ "collision",
42
+ d3.forceCollide()
43
+ .radius(d => {
44
+ // @ts-ignore
45
+ return d.r + fullParams.force!.collisionSpacing
46
+ })
47
+ // .strength(0.01)
48
+ )
49
+ .force("charge", d3.forceManyBody().strength((d) => {
50
+ // @ts-ignore
51
+ return - Math.pow(d.r, 2.0) * fullParams.force!.strength
52
+ }))
53
+ // .force("x", d3.forceX().strength(forceStrength).x(this.graphicWidth / 2))
54
+ // .force("y", d3.forceY().strength(forceStrength).y(this.graphicHeight / 2))
55
+ .on("tick", () => {
56
+ // if (!bubblesSelection) {
57
+ // return
58
+ // }
59
+ bubblesSelection
60
+ .attr("transform", (d) => {
61
+ return `translate(${d.x},${d.y})`
62
+ })
63
+ // .attr("cx", (d) => d.x)
64
+ // .attr("cy", (d) => d.y)
65
+ })
66
+ }
67
+
68
+
69
+ // 計算最大泡泡的半徑
70
+ function getMaxR ({ data, bubbleGroupR, maxValue, avgValue }: {
71
+ data: DatumValue[]
72
+ bubbleGroupR: number
73
+ maxValue: number
74
+ avgValue: number
75
+ }) {
76
+ // 平均r(假想是正方型來計算的,比如說大正方型裡有4個正方型,則 r = width/Math.sqrt(4)/2)
77
+ const avgR = bubbleGroupR / Math.sqrt(data.length)
78
+ const avgSize = avgR * avgR * Math.PI
79
+ const sizeRate = avgSize / avgValue
80
+ const maxSize = maxValue * sizeRate
81
+ const maxR = Math.pow(maxSize / Math.PI, 0.5)
82
+
83
+ const modifier = 0.75 // @Q@ 因為以下公式是假設泡泡是正方型來計算,所以畫出來的圖會偏大一些,這個數值是用來修正用的
84
+ return maxR * modifier
85
+ }
86
+
87
+ function createBubblesData ({ data, LastBubbleDataMap, graphicWidth, graphicHeight, SeriesContainerPositionMap, scaleType }: {
88
+ data: ComputedDataSeries
89
+ LastBubbleDataMap: Map<string, BubblesDatum>
90
+ graphicWidth: number
91
+ graphicHeight: number
92
+ SeriesContainerPositionMap: Map<string, SeriesContainerPosition>
93
+ scaleType: BubbleScaleType
94
+ // highlightIds: string[]
95
+ }) {
96
+ const bubbleGroupR = Math.min(...[graphicWidth, graphicHeight]) / 2
97
+
98
+ const filteredData = data
99
+ .flat()
100
+ .filter(_d => _d.value != null && _d.visible != false)
101
+
102
+ const maxValue = Math.max(
103
+ ...filteredData.map(_d => _d.value!)
104
+ )
105
+
106
+ const avgValue = (
107
+ filteredData.reduce((prev, current) => prev + (current.value ?? 0), 0)
108
+ ) / filteredData.length
109
+
110
+ const maxR = getMaxR({ data: filteredData, bubbleGroupR, maxValue, avgValue })
111
+
112
+ const exponent = scaleType === 'area'
113
+ ? 0.5 // 比例映射面積(0.5為取平方根)
114
+ : 1 // 比例映射半徑
115
+
116
+ const scaleBubbleR = d3.scalePow()
117
+ .domain([0, maxValue])
118
+ .range([0, maxR])
119
+ .exponent(exponent)
120
+
121
+ const bubbleData: BubblesDatum[] = filteredData.map((_d) => {
122
+ const d: BubblesDatum = _d as BubblesDatum
123
+
124
+ const existDatum = LastBubbleDataMap.get(_d.id)
125
+
126
+ if (existDatum) {
127
+ // 使用現有的座標
128
+ d.x = existDatum.x
129
+ d.y = existDatum.y
130
+ } else {
131
+ const seriesContainerPosition = SeriesContainerPositionMap.get(d.seriesLabel)!
132
+ d.x = Math.random() * seriesContainerPosition.width
133
+ d.y = Math.random() * seriesContainerPosition.height
134
+ }
135
+ const r = scaleBubbleR!(d.value ?? 0)!
136
+ d.r = r
137
+ d._originR = r
138
+
139
+ return d
140
+ })
141
+
142
+ return bubbleData
143
+ }
144
+
145
+ function renderBubbles ({ graphicSelection, bubblesData, fullParams }: {
146
+ graphicSelection: d3.Selection<SVGGElement, any, any, any>
147
+ bubblesData: BubblesDatum[]
148
+ fullParams: BubblesParams
149
+ }) {
150
+ let update = graphicSelection.selectAll<SVGGElement, BubblesDatum>("g")
151
+ .data(bubblesData, (d) => d.id)
152
+ let enter = update.enter()
153
+ .append<SVGGElement>("g")
154
+ .attr('cursor', 'pointer')
155
+ enter
156
+ .attr('font-size', 12)
157
+ .style('fill', '#ffffff')
158
+ .attr("text-anchor", "middle")
159
+ .attr("transform", (d) => {
160
+ return `translate(${d.x},${d.y})`
161
+ })
162
+ // .attr("cx", (d) => d.x)
163
+ // .attr("cy", (d) => d.y)
164
+
165
+ enter
166
+ .append("circle")
167
+ .attr("class", "node")
168
+ // update.merge(enter)
169
+ .attr("cx", 0)
170
+ .attr("cy", 0)
171
+ // .attr("r", 1e-6)
172
+ .attr('fill', (d) => d.color)
173
+ // .transition()
174
+ // .duration(500)
175
+
176
+ enter
177
+ .append('text')
178
+ .style('opacity', 0.8)
179
+ .attr('pointer-events', 'none')
180
+
181
+ update.exit().remove()
182
+
183
+ const bubblesSelection = update.merge(enter)
184
+
185
+ bubblesSelection.select('circle')
186
+ .transition()
187
+ .duration(200)
188
+ .attr("r", (d) => d.r)
189
+ .attr('fill', (d) => d.color)
190
+ bubblesSelection
191
+ .each((d,i,g) => {
192
+ const gSelection = d3.select(g[i])
193
+ let breakAll = true
194
+ if (d.label.length <= fullParams.bubbleText.lineLengthMin) {
195
+ breakAll = false
196
+ }
197
+ gSelection.call(renderCircleText, {
198
+ text: d.label,
199
+ radius: d.r * fullParams.bubbleText.fillRate,
200
+ lineHeight: fullParams.bubbleText.lineHeight,
201
+ isBreakAll: breakAll
202
+ })
203
+
204
+ })
205
+
206
+ return bubblesSelection
207
+ }
208
+
209
+ function setHighlightData ({ data, highlightRIncrease, highlightIds }: {
210
+ data: BubblesDatum[]
211
+ // fullParams: BubblesParams
212
+ highlightRIncrease: number
213
+ highlightIds: string[]
214
+ }) {
215
+ if (highlightRIncrease == 0) {
216
+ return
217
+ }
218
+ if (!highlightIds.length) {
219
+ data.forEach(d => d.r = d._originR)
220
+ return
221
+ }
222
+ data.forEach(d => {
223
+ if (highlightIds.includes(d.id)) {
224
+ d.r = d._originR + highlightRIncrease
225
+ } else {
226
+ d.r = d._originR
227
+ }
228
+ })
229
+ }
230
+
231
+ function drag (): d3.DragBehavior<Element, unknown, unknown> {
232
+ return d3.drag()
233
+ .on("start", (event, d: any) => {
234
+ if (!event.active) {
235
+ force!.alpha(1).restart();
236
+ }
237
+ d.fx = d.x
238
+ d.fy = d.y
239
+ })
240
+ .on("drag", (event, d: any) => {
241
+ if (!event.active) {
242
+ force!.alphaTarget(0)
243
+ }
244
+ d.fx = event.x
245
+ d.fy = event.y
246
+ })
247
+ .on("end", (event, d: any) => {
248
+ d.fx = null
249
+ d.fy = null
250
+ })
251
+ }
252
+
253
+
254
+ // private nodeTypePos (d: any) {
255
+ // console.log(d)
256
+ // console.log(this.TypeCenters.get(d.type)!)
257
+ // const typeCenter = this.TypeCenters.get(d.type)!
258
+ // return typeCenter ? typeCenter.x : 0
259
+ // }
260
+
261
+ function groupBubbles ({ fullParams, SeriesContainerPositionMap }: {
262
+ fullParams: BubblesParams
263
+ // graphicWidth: number
264
+ // graphicHeight: number
265
+ SeriesContainerPositionMap: Map<string, SeriesContainerPosition>
266
+ }) {
267
+ // console.log('groupBubbles')
268
+ force!
269
+ // .force('x', d3.forceX().strength(fullParams.force.strength).x(graphicWidth / 2))
270
+ // .force('y', d3.forceY().strength(fullParams.force.strength).y(graphicHeight / 2))
271
+ .force('x', d3.forceX().strength(fullParams.force.strength).x((data: BubblesSimulationDatum) => {
272
+ return SeriesContainerPositionMap.get(data.seriesLabel)!.centerX
273
+ }))
274
+ .force('y', d3.forceY().strength(fullParams.force.strength).y((data: BubblesSimulationDatum) => {
275
+ return SeriesContainerPositionMap.get(data.seriesLabel)!.centerY
276
+ }))
277
+
278
+ force!.alpha(1).restart();
279
+ }
280
+
281
+ function highlight ({ bubblesSelection, highlightIds, fullChartParams }: {
282
+ bubblesSelection: d3.Selection<SVGGElement, BubblesDatum, any, any>
283
+ fullChartParams: ChartParams
284
+ highlightIds: string[]
285
+ }) {
286
+ bubblesSelection.interrupt('highlight')
287
+
288
+ if (!highlightIds.length) {
289
+ bubblesSelection
290
+ .transition('highlight')
291
+ .style('opacity', 1)
292
+ return
293
+ }
294
+
295
+ bubblesSelection.each((d, i, n) => {
296
+ const segment = d3.select(n[i])
297
+
298
+ if (highlightIds.includes(d.id)) {
299
+ segment
300
+ .style('opacity', 1)
301
+ .transition('highlight')
302
+ .ease(d3.easeElastic)
303
+ .duration(500)
304
+ } else {
305
+ // 取消放大
306
+ segment
307
+ .style('opacity', fullChartParams.styles.unhighlightedOpacity)
308
+ }
309
+ })
310
+ }
311
+
312
+ export const Bubbles = defineSeriesPlugin('Bubbles', DEFAULT_BUBBLES_PARAMS)(({ selection, name, observer, subject }) => {
313
+
314
+ const destroy$ = new Subject()
315
+
316
+ const graphicSelection: d3.Selection<SVGGElement, any, any, any> = selection.append('g')
317
+ const bubblesSelection$: Subject<d3.Selection<SVGGElement, BubblesDatum, any, any>> = new Subject()
318
+ // 紀錄前一次bubble data
319
+ let LastBubbleDataMap: Map<string, BubblesDatum> = new Map()
320
+
321
+
322
+ // fullParams$.subscribe(d => {
323
+ // force = makeForce(bubblesSelection, d)
324
+ // })
325
+
326
+ // observer.seriesContainerPosition$.subscribe(d => {
327
+ // console.log(d)
328
+ // })
329
+
330
+ // observer.layout$
331
+ // .pipe(
332
+ // first()
333
+ // )
334
+ // .subscribe(size => {
335
+ // selection
336
+ // .attr('transform', `translate(${size.width / 2}, ${size.height / 2})`)
337
+ // observer.layout$
338
+ // .pipe(
339
+ // takeUntil(destroy$)
340
+ // )
341
+ // .subscribe(size => {
342
+ // selection
343
+ // .transition()
344
+ // .attr('transform', `translate(${size.width / 2}, ${size.height / 2})`)
345
+ // })
346
+ // })
347
+
348
+ // const bubbleGroupR$ = layout$.pipe(
349
+ // map(d => {
350
+ // const minWidth = Math.min(...[d.width, d.height])
351
+ // return minWidth / 2
352
+ // })
353
+ // )
354
+
355
+ // const maxValue$ = computedData$.pipe(
356
+ // map(d => Math.max(
357
+ // ...d
358
+ // .flat()
359
+ // .filter(_d => _d.value != null)
360
+ // .map(_d => _d.value!)
361
+ // )
362
+ // )
363
+ // )
364
+
365
+ // const avgValue$ = computedData$.pipe(
366
+ // map(d => {
367
+ // const total = d
368
+ // .flat()
369
+ // .reduce((prev, current) => prev + (current.value ?? 0), 0)
370
+ // return total / d.length
371
+ // })
372
+ // )
373
+
374
+ // const SeriesDataMap$ = observer.computedData$.pipe(
375
+ // takeUntil(destroy$),
376
+ // map(d => makeSeriesDataMap(d))
377
+ // )
378
+
379
+ const scaleType$ = observer.fullParams$.pipe(
380
+ takeUntil(destroy$),
381
+ map(d => d.bubbleScaleType),
382
+ distinctUntilChanged()
383
+ )
384
+
385
+ const bubblesData$ = new Observable<BubblesDatum[]>(subscriber => {
386
+ combineLatest({
387
+ layout: observer.layout$,
388
+ SeriesContainerPositionMap: observer.SeriesContainerPositionMap$,
389
+ visibleComputedLayoutData: observer.visibleComputedLayoutData$,
390
+ scaleType: scaleType$
391
+ }).pipe(
392
+ takeUntil(destroy$),
393
+ switchMap(async (d) => d),
394
+ ).subscribe(data => {
395
+ // console.log(data.visibleComputedLayoutData)
396
+ const bubblesData = createBubblesData({
397
+ data: data.visibleComputedLayoutData,
398
+ LastBubbleDataMap,
399
+ graphicWidth: data.layout.width,
400
+ graphicHeight: data.layout.height,
401
+ SeriesContainerPositionMap: data.SeriesContainerPositionMap,
402
+ scaleType: data.scaleType
403
+ })
404
+ subscriber.next(bubblesData)
405
+ })
406
+ })
407
+
408
+ // 紀錄前一次bubble data
409
+ bubblesData$.subscribe(d => {
410
+ LastBubbleDataMap = new Map(d.map(_d => [_d.id, _d])) // key: id, value: datum
411
+ })
412
+
413
+ const highlightTarget$ = observer.fullChartParams$.pipe(
414
+ takeUntil(destroy$),
415
+ map(d => d.highlightTarget),
416
+ distinctUntilChanged()
417
+ )
418
+
419
+ combineLatest({
420
+ layout: observer.layout$,
421
+ computedData: observer.computedData$,
422
+ bubblesData: bubblesData$,
423
+ SeriesDataMap: observer.SeriesDataMap$,
424
+ fullParams: observer.fullParams$,
425
+ highlightTarget: highlightTarget$,
426
+ SeriesContainerPositionMap: observer.SeriesContainerPositionMap$,
427
+ // fullChartParams: fullChartParams$
428
+ // highlight: highlight$
429
+ }).pipe(
430
+ takeUntil(destroy$),
431
+ switchMap(async (d) => d),
432
+ ).subscribe(data => {
433
+
434
+ const bubblesSelection = renderBubbles({
435
+ graphicSelection,
436
+ bubblesData: data.bubblesData,
437
+ fullParams: data.fullParams
438
+ })
439
+
440
+ force = makeForce(bubblesSelection, data.fullParams)
441
+
442
+ bubblesSelection
443
+ .on('mouseover', (event, datum) => {
444
+ // this.tooltip!.setDatum({
445
+ // data: d,
446
+ // x: d3.event.clientX,
447
+ // y: d3.event.clientY
448
+ // })
449
+
450
+ subject.event$.next({
451
+ type: 'series',
452
+ eventName: 'mouseover',
453
+ pluginName: name,
454
+ highlightTarget: data.highlightTarget,
455
+ datum,
456
+ series: data.SeriesDataMap.get(datum.seriesLabel)!,
457
+ seriesIndex: datum.seriesIndex,
458
+ seriesLabel: datum.seriesLabel,
459
+ event,
460
+ data: data.computedData
461
+ })
462
+ })
463
+ .on('mousemove', (event, datum) => {
464
+ // this.tooltip!.setDatum({
465
+ // x: d3.event.clientX,
466
+ // y: d3.event.clientY
467
+ // })
468
+
469
+ subject.event$.next({
470
+ type: 'series',
471
+ eventName: 'mousemove',
472
+ pluginName: name,
473
+ highlightTarget: data.highlightTarget,
474
+ datum,
475
+ series: data.SeriesDataMap.get(datum.seriesLabel)!,
476
+ seriesIndex: datum.seriesIndex,
477
+ seriesLabel: datum.seriesLabel,
478
+ event,
479
+ data: data.computedData
480
+ })
481
+ })
482
+ .on('mouseout', (event, datum) => {
483
+ // this.tooltip!.remove()
484
+
485
+ subject.event$.next({
486
+ type: 'series',
487
+ eventName: 'mouseout',
488
+ pluginName: name,
489
+ highlightTarget: data.highlightTarget,
490
+ datum,
491
+ series: data.SeriesDataMap.get(datum.seriesLabel)!,
492
+ seriesIndex: datum.seriesIndex,
493
+ seriesLabel: datum.seriesLabel,
494
+ event,
495
+ data: data.computedData
496
+ })
497
+ })
498
+ .on('click', (event, datum) => {
499
+
500
+ subject.event$.next({
501
+ type: 'series',
502
+ eventName: 'click',
503
+ pluginName: name,
504
+ highlightTarget: data.highlightTarget,
505
+ datum,
506
+ series: data.SeriesDataMap.get(datum.seriesLabel)!,
507
+ seriesIndex: datum.seriesIndex,
508
+ seriesLabel: datum.seriesLabel,
509
+ event,
510
+ data: data.computedData
511
+ })
512
+ })
513
+ .call(drag() as any)
514
+
515
+ force!.nodes(data.bubblesData);
516
+
517
+ groupBubbles({
518
+ fullParams: data.fullParams,
519
+ SeriesContainerPositionMap: data.SeriesContainerPositionMap
520
+ // graphicWidth: data.layout.width,
521
+ // graphicHeight: data.layout.height
522
+ })
523
+
524
+ bubblesSelection$.next(bubblesSelection)
525
+ })
526
+
527
+ combineLatest({
528
+ bubblesSelection: bubblesSelection$,
529
+ bubblesData: bubblesData$,
530
+ highlight: observer.seriesHighlight$.pipe(
531
+ map(data => data.map(d => d.id))
532
+ ),
533
+ fullChartParams: observer.fullChartParams$,
534
+ fullParams: observer.fullParams$,
535
+ // layout: observer.layout$,
536
+ SeriesContainerPositionMap: observer.SeriesContainerPositionMap$,
537
+ }).pipe(
538
+ takeUntil(destroy$),
539
+ switchMap(async d => d)
540
+ ).subscribe(data => {
541
+ highlight({
542
+ bubblesSelection: data.bubblesSelection,
543
+ highlightIds: data.highlight,
544
+ fullChartParams: data.fullChartParams
545
+ })
546
+
547
+ if (data.fullParams.highlightRIncrease) {
548
+ setHighlightData ({
549
+ data: data.bubblesData,
550
+ highlightRIncrease: data.fullParams.highlightRIncrease,
551
+ highlightIds: data.highlight
552
+ })
553
+ renderBubbles({
554
+ graphicSelection,
555
+ bubblesData: data.bubblesData,
556
+ fullParams: data.fullParams
557
+ })
558
+ }
559
+
560
+ groupBubbles({
561
+ fullParams: data.fullParams,
562
+ SeriesContainerPositionMap: data.SeriesContainerPositionMap
563
+ // graphicWidth: data.layout.width,
564
+ // graphicHeight: data.layout.height
565
+ })
566
+ force!.nodes(data.bubblesData);
567
+ })
568
+
569
+ return () => {
570
+ destroy$.next(undefined)
571
+ }
552
572
  })