@orbcharts/plugin-basic 4.0.0-pre-alpha.0

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 (208) hide show
  1. package/LICENSE +201 -0
  2. package/dist/orbcharts-plugin-basic.es.js +25335 -0
  3. package/dist/orbcharts-plugin-basic.umd.js +341 -0
  4. package/dist/plugin-basic/src/baseLayers/BaseBars.d.ts +38 -0
  5. package/dist/plugin-basic/src/baseLayers/BaseBarsTriangle.d.ts +37 -0
  6. package/dist/plugin-basic/src/baseLayers/BaseCategoryAxis.d.ts +42 -0
  7. package/dist/plugin-basic/src/baseLayers/BaseDots.d.ts +38 -0
  8. package/dist/plugin-basic/src/baseLayers/BaseLegend.d.ts +31 -0
  9. package/dist/plugin-basic/src/baseLayers/BaseLineAreas.d.ts +36 -0
  10. package/dist/plugin-basic/src/baseLayers/BaseLines.d.ts +36 -0
  11. package/dist/plugin-basic/src/baseLayers/BaseStackedBars.d.ts +41 -0
  12. package/dist/plugin-basic/src/baseLayers/BaseTooltip.d.ts +47 -0
  13. package/dist/plugin-basic/src/baseLayers/BaseValueAxis.d.ts +38 -0
  14. package/dist/plugin-basic/src/baseLayers/BaseXAxis.d.ts +25 -0
  15. package/dist/plugin-basic/src/baseLayers/BaseXZoom.d.ts +22 -0
  16. package/dist/plugin-basic/src/baseLayers/BaseYAxis.d.ts +23 -0
  17. package/dist/plugin-basic/src/baseLayers/types.d.ts +171 -0
  18. package/dist/plugin-basic/src/const/layerIndex.d.ts +10 -0
  19. package/dist/plugin-basic/src/const/sharedPluginParams.d.ts +6 -0
  20. package/dist/plugin-basic/src/index.d.ts +2 -0
  21. package/dist/plugin-basic/src/plugins/CompositionPlot/CompositionPlot.d.ts +22 -0
  22. package/dist/plugin-basic/src/plugins/CompositionPlot/contextObservables.d.ts +40 -0
  23. package/dist/plugin-basic/src/plugins/CompositionPlot/defaults.d.ts +10 -0
  24. package/dist/plugin-basic/src/plugins/CompositionPlot/index.d.ts +3 -0
  25. package/dist/plugin-basic/src/plugins/CompositionPlot/layers/Bubbles.d.ts +16 -0
  26. package/dist/plugin-basic/src/plugins/CompositionPlot/layers/Indicator.d.ts +0 -0
  27. package/dist/plugin-basic/src/plugins/CompositionPlot/layers/Pie.d.ts +16 -0
  28. package/dist/plugin-basic/src/plugins/CompositionPlot/layers/PieEventTexts.d.ts +16 -0
  29. package/dist/plugin-basic/src/plugins/CompositionPlot/layers/PieLabels.d.ts +16 -0
  30. package/dist/plugin-basic/src/plugins/CompositionPlot/layers/Rose.d.ts +16 -0
  31. package/dist/plugin-basic/src/plugins/CompositionPlot/layers/RoseLabels.d.ts +16 -0
  32. package/dist/plugin-basic/src/plugins/CompositionPlot/layers/Waffle.d.ts +0 -0
  33. package/dist/plugin-basic/src/plugins/CompositionPlot/types.d.ts +110 -0
  34. package/dist/plugin-basic/src/plugins/CompositionPlot/utils.d.ts +19 -0
  35. package/dist/plugin-basic/src/plugins/HierarchyPlot/HierarchyPlot.d.ts +22 -0
  36. package/dist/plugin-basic/src/plugins/HierarchyPlot/contextObservables.d.ts +16 -0
  37. package/dist/plugin-basic/src/plugins/HierarchyPlot/defaults.d.ts +4 -0
  38. package/dist/plugin-basic/src/plugins/HierarchyPlot/index.d.ts +3 -0
  39. package/dist/plugin-basic/src/plugins/HierarchyPlot/layers/TreeMap.d.ts +16 -0
  40. package/dist/plugin-basic/src/plugins/HierarchyPlot/types.d.ts +29 -0
  41. package/dist/plugin-basic/src/plugins/Legend/Legend.d.ts +22 -0
  42. package/dist/plugin-basic/src/plugins/Legend/contextObservables.d.ts +9 -0
  43. package/dist/plugin-basic/src/plugins/Legend/defaults.d.ts +4 -0
  44. package/dist/plugin-basic/src/plugins/Legend/index.d.ts +3 -0
  45. package/dist/plugin-basic/src/plugins/Legend/layers/Legend.d.ts +16 -0
  46. package/dist/plugin-basic/src/plugins/Legend/types.d.ts +31 -0
  47. package/dist/plugin-basic/src/plugins/Legend/utils.d.ts +19 -0
  48. package/dist/plugin-basic/src/plugins/NetworkPlot/NetworkPlot.d.ts +22 -0
  49. package/dist/plugin-basic/src/plugins/NetworkPlot/contextObservables.d.ts +19 -0
  50. package/dist/plugin-basic/src/plugins/NetworkPlot/defaults.d.ts +5 -0
  51. package/dist/plugin-basic/src/plugins/NetworkPlot/index.d.ts +3 -0
  52. package/dist/plugin-basic/src/plugins/NetworkPlot/layers/ForceDirected.d.ts +16 -0
  53. package/dist/plugin-basic/src/plugins/NetworkPlot/layers/ForceDirectedBubbles.d.ts +16 -0
  54. package/dist/plugin-basic/src/plugins/NetworkPlot/types.d.ts +117 -0
  55. package/dist/plugin-basic/src/plugins/ScatterPlot/ScatterPlot.d.ts +22 -0
  56. package/dist/plugin-basic/src/plugins/ScatterPlot/contextObservables.d.ts +140 -0
  57. package/dist/plugin-basic/src/plugins/ScatterPlot/defaults.d.ts +8 -0
  58. package/dist/plugin-basic/src/plugins/ScatterPlot/index.d.ts +3 -0
  59. package/dist/plugin-basic/src/plugins/ScatterPlot/layers/Scatter.d.ts +16 -0
  60. package/dist/plugin-basic/src/plugins/ScatterPlot/layers/ScatterBubbles.d.ts +16 -0
  61. package/dist/plugin-basic/src/plugins/ScatterPlot/layers/XYAux.d.ts +16 -0
  62. package/dist/plugin-basic/src/plugins/ScatterPlot/layers/XYAxes.d.ts +16 -0
  63. package/dist/plugin-basic/src/plugins/ScatterPlot/layers/XZoom.d.ts +16 -0
  64. package/dist/plugin-basic/src/plugins/ScatterPlot/types.d.ts +146 -0
  65. package/dist/plugin-basic/src/plugins/SeriesPlot/SeriesPlot.d.ts +22 -0
  66. package/dist/plugin-basic/src/plugins/SeriesPlot/contextObservables.d.ts +77 -0
  67. package/dist/plugin-basic/src/plugins/SeriesPlot/defaults.d.ts +15 -0
  68. package/dist/plugin-basic/src/plugins/SeriesPlot/index.d.ts +3 -0
  69. package/dist/plugin-basic/src/plugins/SeriesPlot/layers/Bars.d.ts +16 -0
  70. package/dist/plugin-basic/src/plugins/SeriesPlot/layers/BarsPN.d.ts +16 -0
  71. package/dist/plugin-basic/src/plugins/SeriesPlot/layers/BarsTriangle.d.ts +16 -0
  72. package/dist/plugin-basic/src/plugins/SeriesPlot/layers/CategoryAux.d.ts +16 -0
  73. package/dist/plugin-basic/src/plugins/SeriesPlot/layers/CategoryAxis.d.ts +16 -0
  74. package/dist/plugin-basic/src/plugins/SeriesPlot/layers/CategoryZoom.d.ts +16 -0
  75. package/dist/plugin-basic/src/plugins/SeriesPlot/layers/Dots.d.ts +16 -0
  76. package/dist/plugin-basic/src/plugins/SeriesPlot/layers/LineAreas.d.ts +16 -0
  77. package/dist/plugin-basic/src/plugins/SeriesPlot/layers/Lines.d.ts +16 -0
  78. package/dist/plugin-basic/src/plugins/SeriesPlot/layers/StackedBars.d.ts +16 -0
  79. package/dist/plugin-basic/src/plugins/SeriesPlot/layers/StackedValueAxis.d.ts +16 -0
  80. package/dist/plugin-basic/src/plugins/SeriesPlot/layers/ValueAxis.d.ts +16 -0
  81. package/dist/plugin-basic/src/plugins/SeriesPlot/types.d.ts +140 -0
  82. package/dist/plugin-basic/src/plugins/Tooltip/Tooltip.d.ts +22 -0
  83. package/dist/plugin-basic/src/plugins/Tooltip/contextObservables.d.ts +9 -0
  84. package/dist/plugin-basic/src/plugins/Tooltip/defaults.d.ts +4 -0
  85. package/dist/plugin-basic/src/plugins/Tooltip/index.d.ts +3 -0
  86. package/dist/plugin-basic/src/plugins/Tooltip/layers/Tooltip.d.ts +16 -0
  87. package/dist/plugin-basic/src/plugins/Tooltip/types.d.ts +35 -0
  88. package/dist/plugin-basic/src/plugins/Tooltip/utils.d.ts +19 -0
  89. package/dist/plugin-basic/src/plugins/index.d.ts +7 -0
  90. package/dist/plugin-basic/src/types/BaseLayer.d.ts +3 -0
  91. package/dist/plugin-basic/src/types/Common.d.ts +14 -0
  92. package/dist/plugin-basic/src/types/ComputedData.d.ts +27 -0
  93. package/dist/plugin-basic/src/types/PluginParams.d.ts +66 -0
  94. package/dist/plugin-basic/src/types/index.d.ts +3 -0
  95. package/dist/plugin-basic/src/utils/commonUtils.d.ts +3 -0
  96. package/dist/plugin-basic/src/utils/d3Graphics.d.ts +24 -0
  97. package/dist/plugin-basic/src/utils/d3Scale.d.ts +28 -0
  98. package/dist/plugin-basic/src/utils/d3Utils.d.ts +14 -0
  99. package/dist/plugin-basic/src/utils/graphObservables.d.ts +0 -0
  100. package/dist/plugin-basic/src/utils/gridObservables.d.ts +51 -0
  101. package/dist/plugin-basic/src/utils/multivariateObservables.d.ts +74 -0
  102. package/dist/plugin-basic/src/utils/observables.d.ts +34 -0
  103. package/dist/plugin-basic/src/utils/orbchartsUtils.d.ts +26 -0
  104. package/dist/plugin-basic/src/utils/seriesObservables.d.ts +22 -0
  105. package/dist/plugin-basic/vite.config.d.ts +2 -0
  106. package/dist/src/index.d.ts +1 -0
  107. package/package.json +62 -0
  108. package/src/baseLayers/BaseBars.ts +783 -0
  109. package/src/baseLayers/BaseBarsTriangle.ts +692 -0
  110. package/src/baseLayers/BaseCategoryAxis.ts +708 -0
  111. package/src/baseLayers/BaseDots.ts +495 -0
  112. package/src/baseLayers/BaseLegend.ts +684 -0
  113. package/src/baseLayers/BaseLineAreas.ts +644 -0
  114. package/src/baseLayers/BaseLines.ts +721 -0
  115. package/src/baseLayers/BaseStackedBars.ts +818 -0
  116. package/src/baseLayers/BaseTooltip.ts +435 -0
  117. package/src/baseLayers/BaseValueAxis.ts +612 -0
  118. package/src/baseLayers/BaseXAxis.ts +412 -0
  119. package/src/baseLayers/BaseXZoom.ts +250 -0
  120. package/src/baseLayers/BaseYAxis.ts +371 -0
  121. package/src/baseLayers/types.ts +174 -0
  122. package/src/const/layerIndex.ts +36 -0
  123. package/src/const/sharedPluginParams.ts +29 -0
  124. package/src/index.ts +3 -0
  125. package/src/plugins/CompositionPlot/CompositionPlot.ts +308 -0
  126. package/src/plugins/CompositionPlot/contextObservables.ts +251 -0
  127. package/src/plugins/CompositionPlot/defaults.ts +162 -0
  128. package/src/plugins/CompositionPlot/index.ts +3 -0
  129. package/src/plugins/CompositionPlot/layers/Bubbles.ts +808 -0
  130. package/src/plugins/CompositionPlot/layers/Indicator.ts +0 -0
  131. package/src/plugins/CompositionPlot/layers/Pie.ts +776 -0
  132. package/src/plugins/CompositionPlot/layers/PieEventTexts.ts +326 -0
  133. package/src/plugins/CompositionPlot/layers/PieLabels.ts +651 -0
  134. package/src/plugins/CompositionPlot/layers/Rose.ts +546 -0
  135. package/src/plugins/CompositionPlot/layers/RoseLabels.ts +616 -0
  136. package/src/plugins/CompositionPlot/layers/Waffle.ts +0 -0
  137. package/src/plugins/CompositionPlot/types.ts +129 -0
  138. package/src/plugins/CompositionPlot/utils.ts +53 -0
  139. package/src/plugins/HierarchyPlot/HierarchyPlot.ts +190 -0
  140. package/src/plugins/HierarchyPlot/contextObservables.ts +136 -0
  141. package/src/plugins/HierarchyPlot/defaults.ts +31 -0
  142. package/src/plugins/HierarchyPlot/index.ts +3 -0
  143. package/src/plugins/HierarchyPlot/layers/TreeMap.ts +371 -0
  144. package/src/plugins/HierarchyPlot/types.ts +36 -0
  145. package/src/plugins/Legend/Legend.ts +151 -0
  146. package/src/plugins/Legend/contextObservables.ts +55 -0
  147. package/src/plugins/Legend/defaults.ts +37 -0
  148. package/src/plugins/Legend/index.ts +3 -0
  149. package/src/plugins/Legend/layers/Legend.ts +114 -0
  150. package/src/plugins/Legend/types.ts +45 -0
  151. package/src/plugins/Legend/utils.ts +53 -0
  152. package/src/plugins/NetworkPlot/NetworkPlot.ts +228 -0
  153. package/src/plugins/NetworkPlot/contextObservables.ts +123 -0
  154. package/src/plugins/NetworkPlot/defaults.ts +147 -0
  155. package/src/plugins/NetworkPlot/index.ts +3 -0
  156. package/src/plugins/NetworkPlot/layers/ForceDirected.ts +1048 -0
  157. package/src/plugins/NetworkPlot/layers/ForceDirectedBubbles.ts +1318 -0
  158. package/src/plugins/NetworkPlot/types.ts +146 -0
  159. package/src/plugins/ScatterPlot/ScatterPlot.ts +569 -0
  160. package/src/plugins/ScatterPlot/contextObservables.ts +901 -0
  161. package/src/plugins/ScatterPlot/defaults.ts +212 -0
  162. package/src/plugins/ScatterPlot/index.ts +3 -0
  163. package/src/plugins/ScatterPlot/layers/Scatter.ts +518 -0
  164. package/src/plugins/ScatterPlot/layers/ScatterBubbles.ts +670 -0
  165. package/src/plugins/ScatterPlot/layers/XYAux.ts +686 -0
  166. package/src/plugins/ScatterPlot/layers/XYAxes.ts +205 -0
  167. package/src/plugins/ScatterPlot/layers/XZoom.ts +48 -0
  168. package/src/plugins/ScatterPlot/types.ts +179 -0
  169. package/src/plugins/SeriesPlot/SeriesPlot.ts +494 -0
  170. package/src/plugins/SeriesPlot/contextObservables.ts +726 -0
  171. package/src/plugins/SeriesPlot/defaults.ts +142 -0
  172. package/src/plugins/SeriesPlot/index.ts +3 -0
  173. package/src/plugins/SeriesPlot/layers/Bars.ts +84 -0
  174. package/src/plugins/SeriesPlot/layers/BarsPN.ts +85 -0
  175. package/src/plugins/SeriesPlot/layers/BarsTriangle.ts +89 -0
  176. package/src/plugins/SeriesPlot/layers/CategoryAux.ts +1131 -0
  177. package/src/plugins/SeriesPlot/layers/CategoryAxis.ts +92 -0
  178. package/src/plugins/SeriesPlot/layers/CategoryZoom.ts +233 -0
  179. package/src/plugins/SeriesPlot/layers/Dots.ts +91 -0
  180. package/src/plugins/SeriesPlot/layers/LineAreas.ts +82 -0
  181. package/src/plugins/SeriesPlot/layers/Lines.ts +75 -0
  182. package/src/plugins/SeriesPlot/layers/StackedBars.ts +85 -0
  183. package/src/plugins/SeriesPlot/layers/StackedValueAxis.ts +111 -0
  184. package/src/plugins/SeriesPlot/layers/ValueAxis.ts +111 -0
  185. package/src/plugins/SeriesPlot/types.ts +201 -0
  186. package/src/plugins/Tooltip/Tooltip.ts +159 -0
  187. package/src/plugins/Tooltip/contextObservables.ts +55 -0
  188. package/src/plugins/Tooltip/defaults.ts +458 -0
  189. package/src/plugins/Tooltip/index.ts +3 -0
  190. package/src/plugins/Tooltip/layers/Tooltip.ts +90 -0
  191. package/src/plugins/Tooltip/types.ts +55 -0
  192. package/src/plugins/Tooltip/utils.ts +53 -0
  193. package/src/plugins/index.ts +8 -0
  194. package/src/types/BaseLayer.ts +3 -0
  195. package/src/types/Common.ts +20 -0
  196. package/src/types/ComputedData.ts +55 -0
  197. package/src/types/PluginParams.ts +81 -0
  198. package/src/types/index.ts +3 -0
  199. package/src/utils/commonUtils.ts +31 -0
  200. package/src/utils/d3Graphics.ts +177 -0
  201. package/src/utils/d3Scale.ts +198 -0
  202. package/src/utils/d3Utils.ts +92 -0
  203. package/src/utils/graphObservables.ts +0 -0
  204. package/src/utils/gridObservables.ts +637 -0
  205. package/src/utils/multivariateObservables.ts +790 -0
  206. package/src/utils/observables.ts +357 -0
  207. package/src/utils/orbchartsUtils.ts +335 -0
  208. package/src/utils/seriesObservables.ts +172 -0
@@ -0,0 +1,670 @@
1
+ import * as d3 from 'd3'
2
+ import {
3
+ combineLatest,
4
+ map,
5
+ filter,
6
+ switchMap,
7
+ debounceTime,
8
+ takeUntil,
9
+ distinctUntilChanged,
10
+ shareReplay,
11
+ Observable,
12
+ Subject
13
+ } from 'rxjs'
14
+ import type { Theme, EventData } from '@orbcharts/core'
15
+ import type { ScatterPlotPluginParams, ScatterPlotExtendContext, ScatterBubblesParams, ComputedXYDataMultivariate, ComputedXYDatumMultivariate } from '../types'
16
+ import { defineSVGLayer } from '@orbcharts/core'
17
+ import { validateObject } from '@orbcharts/core'
18
+ import { DEFAULT_SCATTER_BUBBLES_PARAMS } from "../defaults"
19
+ import { multivariateSelectionsObservable } from "../../../utils/multivariateObservables"
20
+ import { getDatumColor } from '../../../utils/orbchartsUtils'
21
+ import { createClassName, createUniID } from '../../../utils/orbchartsUtils'
22
+ import type { ComputedDatumMultivariate, ComputedDatumSeries } from '../../../types/ComputedData'
23
+ import type { ContainerPosition, GraphicStyles, Layout } from '../../../types/PluginParams'
24
+ import { LAYER_INDEX_OF_GRAPHIC_COVER } from '../../../const/layerIndex'
25
+ import { getMinMax } from '../../../utils/commonUtils'
26
+
27
+ type ClipPathDatum = {
28
+ id: string;
29
+ // x: number;
30
+ // y: number;
31
+ width: number;
32
+ height: number;
33
+ }
34
+
35
+ interface BubbleDatum extends ComputedXYDatumMultivariate {
36
+ r: number
37
+ opacity: number
38
+ }
39
+
40
+ const pluginName = 'ScatterPlot'
41
+ const layerName = 'ScatterBubbles'
42
+
43
+ // 調整係數 - 因為圓和圓之間的空隙造成聚合起來的大圓會略大,所以稍作微調
44
+ const adjustmentFactor = 0.9
45
+
46
+ function renderDots ({ graphicGSelection, circleGClassName, circleClassName, bubbleData, layerParams, styles, theme, graphicReverseScale }: {
47
+ graphicGSelection: d3.Selection<SVGGElement, any, any, any>
48
+ circleGClassName: string
49
+ circleClassName: string
50
+ // visibleComputedXYData: ComputedXYDataMultivariate
51
+ bubbleData: BubbleDatum[][]
52
+ layerParams: ScatterBubblesParams
53
+ styles: GraphicStyles
54
+ theme: Theme
55
+ graphicReverseScale: [number, number][]
56
+ }) {
57
+ const createEnterDuration = (enter: d3.Selection<d3.EnterElement, ComputedDatumMultivariate, SVGGElement, any>) => {
58
+ const enterSize = enter.size()
59
+ const eachDuration = styles.transitionDuration / enterSize
60
+ return eachDuration
61
+ }
62
+ // enterDuration
63
+ let enterDuration = 0
64
+
65
+ graphicGSelection
66
+ .each((categoryData, categoryIndex, g) => {
67
+ d3.select(g[categoryIndex])
68
+ .selectAll<SVGGElement, ComputedDatumMultivariate>('g')
69
+ .data(bubbleData[categoryIndex], d => d.id)
70
+ .join(
71
+ enter => {
72
+ // enterDuration
73
+ enterDuration = createEnterDuration(enter)
74
+
75
+ return enter
76
+ .append('g')
77
+ .classed(circleGClassName, true)
78
+ },
79
+ update => update,
80
+ exit => exit.remove()
81
+ )
82
+ .attr('transform', d => `translate(${d.axisX}, ${d.axisY})`)
83
+ .each((d, i, g) => {
84
+ const circle = d3.select(g[i])
85
+ .selectAll('circle')
86
+ .data([d])
87
+ .join(
88
+ enter => {
89
+ return enter
90
+ .append('circle')
91
+ .style('cursor', 'pointer')
92
+ .style('vector-effect', 'non-scaling-stroke')
93
+ .classed(circleClassName, true)
94
+ .attr('opacity', 0)
95
+ .transition()
96
+ .delay((_d, _i) => {
97
+ return i * enterDuration
98
+ })
99
+ .attr('opacity', _d => _d.opacity)
100
+ },
101
+ update => {
102
+ return update
103
+ .transition()
104
+ .duration(50)
105
+ // .attr('cx', d => d.axisX)
106
+ // .attr('cy', d => d.axisY)
107
+ .attr('opacity', _d => _d.opacity)
108
+ },
109
+ exit => exit.remove()
110
+ )
111
+ .attr('r', d => d.r)
112
+ .attr('fill', (d, i) => getDatumColor({ datum: d, colorType: layerParams.fillColorType, theme }))
113
+ .attr('stroke', (d, i) => getDatumColor({ datum: d, colorType: layerParams.strokeColorType, theme }))
114
+ .attr('stroke-width', layerParams.strokeWidth)
115
+ .attr('transform', `scale(${graphicReverseScale[categoryIndex][0] ?? 1}, ${graphicReverseScale[categoryIndex][1] ?? 1})`)
116
+ })
117
+ })
118
+
119
+ const graphicCircleSelection: d3.Selection<SVGRectElement, BubbleDatum, SVGGElement, unknown> = graphicGSelection.selectAll(`circle.${circleClassName}`)
120
+
121
+ return graphicCircleSelection
122
+ }
123
+
124
+ function highlightBubbles ({ selection, ids, styles }: {
125
+ selection: d3.Selection<SVGGElement, BubbleDatum, any, any>
126
+ ids: string[]
127
+ styles: GraphicStyles
128
+ }) {
129
+ selection.interrupt('highlight')
130
+ if (!ids.length) {
131
+ // remove highlight
132
+ selection
133
+ .transition('highlight')
134
+ .duration(200)
135
+ .style('opacity', d => d.opacity)
136
+ // selection
137
+ // .attr('stroke-width', layerParams.strokeWidth)
138
+
139
+ return
140
+ }
141
+
142
+ selection
143
+ .each((d, i, n) => {
144
+ if (ids.includes(d.id)) {
145
+ const dot = d3.select<SVGGElement, BubbleDatum>(n[i])
146
+ dot
147
+ .style('opacity', d => d.opacity)
148
+ .transition('highlight')
149
+ .duration(200)
150
+ // dot
151
+ // .attr('stroke-width', layerParams.strokeWidthWhileHighlight)
152
+ } else {
153
+ const dot = d3.select(n[i])
154
+ dot
155
+ .style('opacity', styles.unhighlightedOpacity)
156
+ .transition('highlight')
157
+ .duration(200)
158
+ // dot
159
+ // .attr('stroke-width', layerParams.strokeWidth)
160
+
161
+ }
162
+ })
163
+ }
164
+
165
+ function renderClipPath ({ defsSelection, clipPathData }: {
166
+ defsSelection: d3.Selection<SVGDefsElement, any, any, any>
167
+ clipPathData: ClipPathDatum[]
168
+ }) {
169
+ const clipPath = defsSelection
170
+ .selectAll<SVGClipPathElement, Layout>('clipPath')
171
+ .data(clipPathData)
172
+ .join(
173
+ enter => {
174
+ return enter
175
+ .append('clipPath')
176
+ },
177
+ update => update,
178
+ exit => exit.remove()
179
+ )
180
+ .attr('id', d => d.id)
181
+ .each((d, i, g) => {
182
+ const rect = d3.select(g[i])
183
+ .selectAll<SVGRectElement, typeof d>('rect')
184
+ .data([d])
185
+ .join('rect')
186
+ .attr('x', 0)
187
+ .attr('y', 0)
188
+ .attr('width', _d => _d.width)
189
+ .attr('height', _d => _d.height)
190
+ })
191
+
192
+ }
193
+
194
+
195
+ export const ScatterBubbles = defineSVGLayer<ScatterPlotExtendContext, ScatterPlotPluginParams, ScatterBubblesParams>({
196
+ name: layerName,
197
+ defaultParams: DEFAULT_SCATTER_BUBBLES_PARAMS,
198
+ layerIndex: LAYER_INDEX_OF_GRAPHIC_COVER,
199
+ initShow: false,
200
+ validator: (params) => {
201
+ const result = validateObject(params, {
202
+ // radius: {
203
+ // toBeTypes: ['number']
204
+ // },
205
+ fillColorType: {
206
+ toBeOption: 'ColorType',
207
+ },
208
+ strokeColorType: {
209
+ toBeOption: 'ColorType',
210
+ },
211
+ strokeWidth: {
212
+ toBeTypes: ['number']
213
+ },
214
+ valueLinearOpacity: {
215
+ toBeTypes: ['number[]']
216
+ },
217
+ arcScaleType: {
218
+ toBe: 'ArcScaleType',
219
+ test: (value) => {
220
+ return value === 'area' || value === 'radius'
221
+ }
222
+ },
223
+ sizeAdjust: {
224
+ toBeTypes: ['number']
225
+ }
226
+ })
227
+ return result
228
+ },
229
+ setup: ({ svgG, pluginParams$, layerParams$, context }) => {
230
+
231
+ // const subscription = layerParams$.subscribe((params) => {
232
+ // // Handle params update
233
+ // })
234
+
235
+ // context.seriesData$.subscribe((data) => {
236
+ // // Handle series data update
237
+ // console.log(data)
238
+ // })
239
+ const destroy$ = new Subject()
240
+
241
+ context.layout$
242
+ .pipe(
243
+ takeUntil(destroy$)
244
+ )
245
+ .subscribe(layout => {
246
+ d3.select(svgG)
247
+ .attr('transform', `translate(${layout.left}, ${layout.top})`)
248
+ })
249
+
250
+ const clipPathID = createUniID(pluginName, layerName, 'clipPath-box')
251
+ const circleGClassName = createClassName(pluginName, layerName, 'circleG')
252
+ const circleClassName = createClassName(pluginName, layerName, 'circle')
253
+
254
+ const {
255
+ seriesSelection$,
256
+ axesSelection$,
257
+ defsSelection$,
258
+ graphicGSelection$
259
+ } = multivariateSelectionsObservable({
260
+ selection: d3.select(svgG),
261
+ pluginName,
262
+ layerName,
263
+ clipPathID,
264
+ seriesLabels$: context.seriesLabels$,
265
+ containerPosition$: context.containerPosition$,
266
+ graphicTransform$: context.graphicTransform$
267
+ })
268
+
269
+ const graphicReverseScale$: Observable<[number, number][]> = combineLatest({
270
+ computedData: context.computedData$,
271
+ graphicReverseScale: context.graphicReverseScale$
272
+ }).pipe(
273
+ takeUntil(destroy$),
274
+ debounceTime(0),
275
+ map(data => {
276
+ return data.computedData.map((series, categoryIndex) => {
277
+ return data.graphicReverseScale[categoryIndex]
278
+ })
279
+ })
280
+ )
281
+
282
+ const clipPathSubscription = combineLatest({
283
+ defsSelection: defsSelection$,
284
+ layout: context.layout$,
285
+ }).pipe(
286
+ takeUntil(destroy$),
287
+ debounceTime(0),
288
+ ).subscribe(data => {
289
+ // 外層的遮罩
290
+ const clipPathData = [{
291
+ id: clipPathID,
292
+ width: data.layout.width,
293
+ height: data.layout.height
294
+ }]
295
+ renderClipPath({
296
+ defsSelection: data.defsSelection,
297
+ clipPathData,
298
+ })
299
+ })
300
+
301
+ const filteredValueList$ = context.filteredXYMinMaxData$.pipe(
302
+ takeUntil(destroy$),
303
+ map(data => data.datumList.flat().map(d => d.multivariate[2]?.value ?? 0)),
304
+ shareReplay(1)
305
+ )
306
+
307
+ const filteredMinMaxValue$ = context.filteredXYMinMaxData$.pipe(
308
+ takeUntil(destroy$),
309
+ map(data => {
310
+ return getMinMax(data.datumList.flat().map(d => d.multivariate[2]?.value ?? 0))
311
+ })
312
+ )
313
+
314
+ const opacityScale$ = combineLatest({
315
+ filteredMinMaxValue: filteredMinMaxValue$,
316
+ layerParams: layerParams$
317
+ }).pipe(
318
+ takeUntil(destroy$),
319
+ debounceTime(0),
320
+ map(data => {
321
+ return d3.scaleLinear()
322
+ .domain(data.filteredMinMaxValue)
323
+ .range(data.layerParams.valueLinearOpacity)
324
+ })
325
+ )
326
+
327
+ // 虛擬大圓(所有小圓聚合起來的大圓)的半徑
328
+ const totalR$ = combineLatest({
329
+ layout: context.layout$,
330
+ layerParams: layerParams$
331
+ }).pipe(
332
+ takeUntil(destroy$),
333
+ map(data => {
334
+ // 場景最短邊
335
+ const fullRadius = Math.min(...[data.layout.width, data.layout.height]) / 2
336
+ return fullRadius * data.layerParams.sizeAdjust
337
+ })
338
+ )
339
+
340
+ const totalValue$ = filteredValueList$.pipe(
341
+ takeUntil(destroy$),
342
+ map(data => {
343
+ return data.reduce((acc, current) => acc + current, 0)
344
+ }),
345
+ filter(data => data > 0) // 避免後續計算scale的時候發生問題
346
+ )
347
+
348
+ const radiusScale$ = combineLatest({
349
+ totalR: totalR$,
350
+ totalValue: totalValue$,
351
+ layerParams: layerParams$
352
+ }).pipe(
353
+ takeUntil(destroy$),
354
+ debounceTime(0),
355
+ map(data => {
356
+ // console.log({ totalR: data.totalR, totalValue: data.totalValue })
357
+ return d3.scalePow()
358
+ .domain([0, data.totalValue])
359
+ .range([0, data.totalR])
360
+ .exponent(data.layerParams.arcScaleType === 'area'
361
+ ? 0.5 // 數值映射面積(0.5為取平方根)
362
+ : 1 // 數值映射半徑
363
+ )
364
+ })
365
+ )
366
+
367
+ const scaleFactor$ = combineLatest({
368
+ radiusScale: radiusScale$,
369
+ layerParams: layerParams$,
370
+ totalR: totalR$,
371
+ filteredValueList: filteredValueList$
372
+ }).pipe(
373
+ takeUntil(destroy$),
374
+ debounceTime(0),
375
+ map(data => {
376
+ return data.layerParams.arcScaleType === 'area'
377
+ ? 1
378
+ // 當數值映射半徑時,多個小圓的總面積會小於大圓的面積,所以要計算縮放比例
379
+ : (() => {
380
+ const totalArea = data.totalR * data.totalR * Math.PI
381
+ return Math.sqrt(totalArea / d3.sum(data.filteredValueList, d => Math.PI * Math.pow(data.radiusScale(d), 2)))
382
+ })()
383
+ })
384
+ )
385
+
386
+ const bubbleData$ = combineLatest({
387
+ visibleComputedXYData: context.visibleComputedXYData$,
388
+ opacityScale: opacityScale$,
389
+ radiusScale: radiusScale$,
390
+ scaleFactor: scaleFactor$,
391
+ layerParams: layerParams$
392
+ }).pipe(
393
+ takeUntil(destroy$),
394
+ debounceTime(0),
395
+ map(data => {
396
+ return data.visibleComputedXYData.map(category => {
397
+ return category.map(_d => {
398
+ const d: BubbleDatum = _d as BubbleDatum
399
+ d.r = data.radiusScale(d.multivariate[2].value) * data.scaleFactor * adjustmentFactor
400
+ d.opacity = data.opacityScale(d.multivariate[2].value)
401
+ return d
402
+ })
403
+ })
404
+ })
405
+ )
406
+
407
+
408
+ const graphicSelection$ = combineLatest({
409
+ graphicGSelection: graphicGSelection$,
410
+ // visibleComputedLayoutData: context.visibleComputedLayoutData$,
411
+ bubbleData: bubbleData$,
412
+ graphicReverseScale: graphicReverseScale$,
413
+ theme: context.theme$,
414
+ styles: pluginParams$.pipe(
415
+ map(params => params.styles)
416
+ ),
417
+ layerParams: layerParams$,
418
+ }).pipe(
419
+ takeUntil(destroy$),
420
+ debounceTime(0),
421
+ map(data => {
422
+ return renderDots({
423
+ graphicGSelection: data.graphicGSelection,
424
+ circleGClassName,
425
+ circleClassName,
426
+ bubbleData: data.bubbleData,
427
+ layerParams: data.layerParams,
428
+ styles: data.styles,
429
+ theme: data.theme,
430
+ graphicReverseScale: data.graphicReverseScale
431
+ })
432
+ })
433
+ )
434
+
435
+ const valueLabels$ = context.encoding$.pipe(
436
+ takeUntil(destroy$),
437
+ map(encoding => {
438
+ return [
439
+ encoding.multivariate[0].name ?? 'X',
440
+ encoding.multivariate[1].name ?? 'Y',
441
+ encoding.multivariate[2].name ?? 'Value'
442
+ ]
443
+ }),
444
+ distinctUntilChanged((a, b) => a[0] === b[0] && a[1] === b[1] && a[2] === b[2])
445
+ )
446
+
447
+ const highlightTarget$ = pluginParams$.pipe(
448
+ takeUntil(destroy$),
449
+ map(d => d.styles.highlightTarget),
450
+ distinctUntilChanged()
451
+ )
452
+
453
+ combineLatest({
454
+ graphicSelection: graphicSelection$,
455
+ computedData: context.computedData$,
456
+ // CategoryDataMap: context.CategoryDataMap$,
457
+ highlightTarget: highlightTarget$,
458
+ valueLabels: valueLabels$
459
+ }).pipe(
460
+ takeUntil(destroy$),
461
+ debounceTime(0),
462
+ ).subscribe(data => {
463
+
464
+ data.graphicSelection
465
+ .on('mouseover', (event, datum) => {
466
+ // event.stopPropagation()
467
+ // console.log({
468
+ // type: 'multivariate',
469
+ // eventName: 'mouseover',
470
+ // pluginName,
471
+ // highlightTarget: data.highlightTarget,
472
+ // datum,
473
+ // category: data.CategoryDataMap.get(datum.categoryLabel)!,
474
+ // categoryIndex: datum.categoryIndex,
475
+ // categoryLabel: datum.categoryLabel,
476
+ // data: data.computedData,
477
+ // event,
478
+ // })
479
+ context.eventTrigger$.next({
480
+ // type: 'multivariate',
481
+ // eventName: 'mouseover',
482
+ // pluginName,
483
+ // highlightTarget: data.highlightTarget,
484
+ // valueDetail: [
485
+ // {
486
+ // value: datum.value[0],
487
+ // valueIndex: 0,
488
+ // valueLabel: data.valueLabels[0]
489
+ // },
490
+ // {
491
+ // value: datum.value[1],
492
+ // valueIndex: 1,
493
+ // valueLabel: data.valueLabels[1]
494
+ // },
495
+ // {
496
+ // value: datum.value[2],
497
+ // valueIndex: 2,
498
+ // valueLabel: data.valueLabels[2]
499
+ // }
500
+ // ],
501
+ // datum,
502
+ // category: data.CategoryDataMap.get(datum.categoryLabel)!,
503
+ // categoryIndex: datum.categoryIndex,
504
+ // categoryLabel: datum.categoryLabel,
505
+ // data: data.computedData,
506
+ // event,
507
+ eventName: 'mouseover',
508
+ pluginName,
509
+ layerName,
510
+ target: datum,
511
+ event
512
+ })
513
+ })
514
+ .on('mousemove', (event, datum) => {
515
+ // event.stopPropagation()
516
+
517
+ context.eventTrigger$.next({
518
+ // type: 'multivariate',
519
+ // eventName: 'mousemove',
520
+ // pluginName,
521
+ // highlightTarget: data.highlightTarget,
522
+ // valueDetail: [
523
+ // {
524
+ // value: datum.value[0],
525
+ // valueIndex: 0,
526
+ // valueLabel: data.valueLabels[0]
527
+ // },
528
+ // {
529
+ // value: datum.value[1],
530
+ // valueIndex: 1,
531
+ // valueLabel: data.valueLabels[1]
532
+ // },
533
+ // {
534
+ // value: datum.value[2],
535
+ // valueIndex: 2,
536
+ // valueLabel: data.valueLabels[2]
537
+ // }
538
+ // ],
539
+ // datum,
540
+ // category: data.CategoryDataMap.get(datum.categoryLabel)!,
541
+ // categoryIndex: datum.categoryIndex,
542
+ // categoryLabel: datum.categoryLabel,
543
+ // data: data.computedData,
544
+ // event,
545
+ eventName: 'mousemove',
546
+ pluginName,
547
+ layerName,
548
+ target: datum,
549
+ event
550
+ })
551
+ })
552
+ .on('mouseout', (event, datum) => {
553
+ // event.stopPropagation()
554
+
555
+ context.eventTrigger$.next({
556
+ // type: 'multivariate',
557
+ // eventName: 'mouseout',
558
+ // pluginName,
559
+ // highlightTarget: data.highlightTarget,
560
+ // valueDetail: [
561
+ // {
562
+ // value: datum.value[0],
563
+ // valueIndex: 0,
564
+ // valueLabel: data.valueLabels[0]
565
+ // },
566
+ // {
567
+ // value: datum.value[1],
568
+ // valueIndex: 1,
569
+ // valueLabel: data.valueLabels[1]
570
+ // },
571
+ // {
572
+ // value: datum.value[2],
573
+ // valueIndex: 2,
574
+ // valueLabel: data.valueLabels[2]
575
+ // }
576
+ // ],
577
+ // datum,
578
+ // category: data.CategoryDataMap.get(datum.categoryLabel)!,
579
+ // categoryIndex: datum.categoryIndex,
580
+ // categoryLabel: datum.categoryLabel,
581
+ // data: data.computedData,
582
+ // event,
583
+ eventName: 'mouseout',
584
+ pluginName,
585
+ layerName,
586
+ target: datum,
587
+ event
588
+ })
589
+ })
590
+ .on('click', (event, datum) => {
591
+ // event.stopPropagation()
592
+
593
+ context.eventTrigger$.next({
594
+ // type: 'multivariate',
595
+ // eventName: 'click',
596
+ // pluginName,
597
+ // highlightTarget: data.highlightTarget,
598
+ // valueDetail: [
599
+ // {
600
+ // value: datum.value[0],
601
+ // valueIndex: 0,
602
+ // valueLabel: data.valueLabels[0]
603
+ // },
604
+ // {
605
+ // value: datum.value[1],
606
+ // valueIndex: 1,
607
+ // valueLabel: data.valueLabels[1]
608
+ // },
609
+ // {
610
+ // value: datum.value[2],
611
+ // valueIndex: 2,
612
+ // valueLabel: data.valueLabels[2]
613
+ // }
614
+ // ],
615
+ // datum,
616
+ // category: data.CategoryDataMap.get(datum.categoryLabel)!,
617
+ // categoryIndex: datum.categoryIndex,
618
+ // categoryLabel: datum.categoryLabel,
619
+ // data: data.computedData,
620
+ // event,
621
+ eventName: 'click',
622
+ pluginName,
623
+ layerName,
624
+ target: datum,
625
+ event
626
+ })
627
+ })
628
+
629
+ })
630
+
631
+ combineLatest({
632
+ graphicSelection: graphicSelection$,
633
+ highlight: context.highlight$.pipe(
634
+ map(data => data.map(d => d.id))
635
+ ),
636
+ styles: pluginParams$.pipe(
637
+ map(params => params.styles)
638
+ ),
639
+ }).pipe(
640
+ takeUntil(destroy$),
641
+ switchMap(async d => d)
642
+ ).subscribe(data => {
643
+ highlightBubbles({
644
+ selection: data.graphicSelection,
645
+ ids: data.highlight,
646
+ styles: data.styles
647
+ })
648
+ })
649
+
650
+ // graphicGSelection$.subscribe(data => {
651
+ // console.log('graphicGSelection$', data)
652
+ // })
653
+
654
+ // context.visibleComputedLayoutData$.subscribe(data => {
655
+ // console.log('visibleComputedLayoutData$', data)
656
+ // })
657
+
658
+ // context.fullChartParams$.subscribe(data => {
659
+ // console.log('fullChartParams$', data)
660
+ // })
661
+
662
+ // layerParams$.subscribe(data => {
663
+ // console.log('layerParams$', data)
664
+ // })
665
+
666
+ return () => {
667
+ destroy$.next(undefined)
668
+ }
669
+ }
670
+ })