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

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 (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
  })