@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,616 @@
1
+ import * as d3 from 'd3'
2
+ import {
3
+ combineLatest,
4
+ map,
5
+ filter,
6
+ switchMap,
7
+ takeUntil,
8
+ distinctUntilChanged,
9
+ shareReplay,
10
+ debounceTime,
11
+ Observable,
12
+ Subject,
13
+ BehaviorSubject} from 'rxjs'
14
+ import type { Theme, EventData } from '@orbcharts/core'
15
+ import type { CompositionPlotExtendContext, CompositionPlotPluginParams, RoseParams, RoseLabelsParams } from "../types"
16
+ import type { PieDatum } from '../utils'
17
+ import { defineSVGLayer } from '@orbcharts/core'
18
+ import { validateObject } from '@orbcharts/core'
19
+ import { DEFAULT_ROSE_LABELS_PARAMS, DEFAULT_ROSE_PARAMS } from "../defaults"
20
+ import { seriesCenterSelectionObservable } from "../../../utils/seriesObservables"
21
+ import { getDatumColor } from '../../../utils/orbchartsUtils'
22
+ import { createClassName } from '../../../utils/orbchartsUtils'
23
+ import { makeD3Arc } from '../../../utils/d3Utils'
24
+ import { makePieData } from '../utils'
25
+ import { renderTspansOnQuadrant } from '../../../utils/d3Graphics'
26
+ import type { ComputedDatumSeries } from '../../../types/ComputedData'
27
+ import type { ContainerPosition } from '../../../types/PluginParams'
28
+ import { LAYER_INDEX_OF_GRAPHIC, LAYER_INDEX_OF_LABEL } from '../../../const/layerIndex'
29
+
30
+ interface RenderDatum {
31
+ pieDatum: PieDatum
32
+ arcIndex: number
33
+ arcLabels: string[]
34
+ lineStartX: number
35
+ lineStartY: number
36
+ lineStartMouseoverX: number
37
+ lineStartMouseoverY: number
38
+ x: number
39
+ y: number
40
+ mouseoverX: number
41
+ mouseoverY: number
42
+ textWidth: number, // 文字寬度
43
+ collisionShiftX: number // 避免碰撞的位移
44
+ collisionShiftY: number
45
+ quadrant: number // 第幾象限
46
+ }
47
+
48
+ const pluginName = 'CompositionPlot'
49
+ const layerName = 'RoseLabels'
50
+
51
+ const labelGClassName = createClassName(pluginName, layerName, 'label-g')
52
+ const lineGClassName = createClassName(pluginName, layerName, 'line-g')
53
+ const textClassName = createClassName(pluginName, layerName, 'text')
54
+
55
+ const pieOuterCentroid = 2
56
+
57
+ function makeRenderData ({ pieData, labelCentroid, arcScaleType, maxValue, axisWidth, outerRadius, lineStartCentroid, layerParams }: {
58
+ pieData: PieDatum[]
59
+ // arc: d3.Arc<any, d3.DefaultArcObject>
60
+ labelCentroid: number
61
+ arcScaleType: 'area' | 'radius'
62
+ maxValue: number
63
+ axisWidth: number
64
+ outerRadius: number
65
+ lineStartCentroid: number
66
+ layerParams: RoseLabelsParams
67
+ }): RenderDatum[] {
68
+
69
+ const outerRadiusWidth = (axisWidth / 2) * outerRadius
70
+
71
+ const exponent = arcScaleType === 'area'
72
+ ? 0.5 // 比例映射面積(0.5為取平方根)
73
+ : 1 // 比例映射半徑
74
+
75
+ const arcScale = d3.scalePow()
76
+ .domain([0, maxValue])
77
+ .range([0, outerRadiusWidth])
78
+ .exponent(exponent)
79
+
80
+ return pieData
81
+ .map((d, i) => {
82
+ const eachOuterRadius = arcScale(d.value)
83
+
84
+ const arc = d3.arc()
85
+ .innerRadius(0)
86
+ .outerRadius(eachOuterRadius)
87
+ .padAngle(0)
88
+ .padRadius(eachOuterRadius)
89
+ .cornerRadius(0)
90
+
91
+ const [_x, _y] = arc!.centroid(d as any)
92
+ const [_mouseoverX, _mouseoverY] = [_x, _y]
93
+ const arcLabel = layerParams.labelFn(d.data)
94
+ return {
95
+ pieDatum: d,
96
+ arcIndex: i,
97
+ arcLabels: arcLabel.split('\n'),
98
+ lineStartX: _x * lineStartCentroid,
99
+ lineStartY: _y * lineStartCentroid,
100
+ lineStartMouseoverX: _mouseoverX * lineStartCentroid,
101
+ lineStartMouseoverY: _mouseoverY * lineStartCentroid,
102
+ x: _x * labelCentroid!,
103
+ y: _y * labelCentroid!,
104
+ mouseoverX: _mouseoverX * labelCentroid!,
105
+ mouseoverY: _mouseoverY * labelCentroid!,
106
+ textWidth: 0, // 後面再做計算
107
+ collisionShiftX: 0, // 後面再做計算
108
+ collisionShiftY: 0, // 後面再做計算
109
+ quadrant: _x >= 0 && _y <= 0
110
+ ? 1
111
+ : _x < 0 && _y <= 0
112
+ ? 2
113
+ : _x < 0 && _y > 0
114
+ ? 3
115
+ : 4
116
+ }
117
+ })
118
+ .filter(d => d.pieDatum.data.visible)
119
+ }
120
+
121
+ // 繪製圓餅圖
122
+ function renderLabel ({ labelGSelection, data, layerParams, pluginParams, theme, fontSizePx }: {
123
+ labelGSelection: d3.Selection<SVGGElement, undefined, any, any>
124
+ data: RenderDatum[]
125
+ layerParams: RoseLabelsParams
126
+ pluginParams: CompositionPlotPluginParams
127
+ theme: Theme
128
+ fontSizePx: number
129
+ }) {
130
+ // console.log(data)
131
+ // let update = this.gSelection.selectAll('g').data(pieData)
132
+ const textSelection = labelGSelection
133
+ .selectAll<SVGTextElement, RenderDatum>('text')
134
+ .data(data, d => d.pieDatum.id)
135
+ .join('text')
136
+ .classed(textClassName, true)
137
+ .attr('font-weight', 'bold')
138
+ .attr('text-anchor', d => d.quadrant == 1 || d.quadrant == 4 ? 'start' : 'end')
139
+ .style('dominant-baseline', d => d.quadrant == 1 || d.quadrant == 2 ? 'auto' : 'hanging')
140
+ // .style('pointer-events', 'none')
141
+ .style('cursor', d => pluginParams.styles.highlightTarget && pluginParams.styles.highlightTarget != 'none'
142
+ ? 'pointer'
143
+ : 'none')
144
+ // .text((d, i) => d.arcLabel)
145
+ // .text(d => layerParams.labelFn(d.pieDatum.data))
146
+ .attr('font-size', fontSizePx)
147
+ .attr('fill', (d, i) => getDatumColor({ datum: d.pieDatum.data, colorType: layerParams.labelColorType, theme }))
148
+ .each((d, i, n) => {
149
+ // const textNode = d3.select<SVGTextElement, RenderDatum>(n[i])
150
+ // .selectAll('tspan')
151
+ // .data(d.arcLabels)
152
+ // .join('tspan')
153
+ // .attr('x', 0)
154
+ // .attr('y', (_d, _i) => d.quadrant == 1 || d.quadrant == 2
155
+ // ? - (d.arcLabels.length - 1 - _i) * fontSizePx
156
+ // : _i * fontSizePx)
157
+ // .text(d => d)
158
+ renderTspansOnQuadrant(d3.select<SVGTextElement, RenderDatum>(n[i]), {
159
+ textArr: d.arcLabels,
160
+ fontSizePx,
161
+ quadrant: d.quadrant
162
+ })
163
+ })
164
+ textSelection
165
+ .transition()
166
+ .attr('transform', (d) => {
167
+ return 'translate(' + d.x + ',' + d.y + ')'
168
+ })
169
+ // .on('end', () => initHighlight({ labelSelection, data, fullChartParams }))
170
+
171
+ // 如無新增資料則不用等動畫
172
+ // if (enter.size() == 0) {
173
+ // this.initHighlight()
174
+ // }
175
+
176
+ return textSelection
177
+ }
178
+
179
+ // 獲取每個文字元素的邊界框並檢查是否重疊
180
+ function resolveCollisions(textSelection: d3.Selection<SVGTextElement, RenderDatum, any, any>, data: RenderDatum[], fontSizePx: number) {
181
+ const textArray = textSelection.nodes();
182
+ const padding = fontSizePx // 調整文字間的間距
183
+
184
+ // 存儲每個標籤的當前位置
185
+ const positions = textArray.map((textNode, i) => {
186
+ const bbox = textNode.getBBox();
187
+ // const arcCentroid = arc.centroid(data[i]);
188
+ const arcCentroid = [data[i].x, data[i].y];
189
+ return {
190
+ node: textNode,
191
+ x: arcCentroid[0],
192
+ y: arcCentroid[1],
193
+ width: bbox.width,
194
+ height: bbox.height
195
+ }
196
+ })
197
+
198
+ // 順時針碰撞檢測(只處理 2、4 象限,將較後面的文字碰撞時往外偏移)
199
+ for (let i = 0; i < positions.length; i++) {
200
+ const a = positions[i]
201
+
202
+ for (let j = i + 1; j < positions.length; j++) {
203
+ const b = positions[j]
204
+
205
+ // 記錄文字寬度
206
+ data[i].textWidth = a.width
207
+
208
+ const ax = a.x + data[i].collisionShiftX
209
+ const ay = a.y + data[i].collisionShiftY
210
+ const bx = b.x + data[j].collisionShiftX
211
+ const by = b.y + data[j].collisionShiftY
212
+
213
+ // 檢查是否重疊
214
+ if (!(ax + a.width / 2 < bx - b.width / 2 ||
215
+ ax - a.width / 2 > bx + b.width / 2 ||
216
+ ay + a.height / 2 < by - b.height / 2 ||
217
+ ay - a.height / 2 > by + b.height / 2)) {
218
+
219
+ if (data[j].quadrant == 2) {
220
+ const moveDown = (by > ay)
221
+ ? -padding * 2
222
+ : -padding
223
+ data[j].collisionShiftY += moveDown // 由後一個累加高度
224
+ } else if (data[j].quadrant == 4) {
225
+ const moveDown = (by > ay)
226
+ ? padding
227
+ : padding * 2
228
+ data[j].collisionShiftY += moveDown // 由後一個累加高度
229
+ }
230
+ }
231
+ }
232
+ }
233
+
234
+ // 逆時針碰撞檢測(只處理 1、3 象限,將較前面的文字碰撞時往外偏移)
235
+ for (let i = positions.length - 1; i >= 0; i--) {
236
+ const a = positions[i]
237
+
238
+ for (let j = i - 1; j >= 0; j--) {
239
+ const b = positions[j]
240
+
241
+ // 記錄文字寬度
242
+ data[i].textWidth = a.width
243
+
244
+ const ax = a.x + data[i].collisionShiftX
245
+ const ay = a.y + data[i].collisionShiftY
246
+ const bx = b.x + data[j].collisionShiftX
247
+ const by = b.y + data[j].collisionShiftY
248
+
249
+ // 檢查是否重疊
250
+ if (!(ax + a.width / 2 < bx - b.width / 2 ||
251
+ ax - a.width / 2 > bx + b.width / 2 ||
252
+ ay + a.height / 2 < by - b.height / 2 ||
253
+ ay - a.height / 2 > by + b.height / 2)) {
254
+
255
+ if (data[j].quadrant == 1) {
256
+ const moveDown = (by > ay)
257
+ ? -padding * 2
258
+ : -padding
259
+ data[j].collisionShiftY += moveDown // 由前一個累加高度
260
+ } else if (data[j].quadrant == 3) {
261
+ const moveDown = (by > ay)
262
+ ? padding
263
+ : padding * 2
264
+ data[j].collisionShiftY += moveDown // 由前一個累加高度
265
+ }
266
+ }
267
+ }
268
+ }
269
+
270
+ // 全部算完再來 render
271
+ textSelection
272
+ .data(data)
273
+ .transition()
274
+ .attr('transform', (d) => {
275
+ return `translate(${d.x + d.collisionShiftX},${d.y + d.collisionShiftY})`
276
+ })
277
+ }
278
+
279
+ function renderLine ({ lineGSelection, data, layerParams, theme }: {
280
+ lineGSelection: d3.Selection<SVGGElement, undefined, any, any>
281
+ data: RenderDatum[]
282
+ layerParams: RoseLabelsParams
283
+ theme: Theme
284
+ }) {
285
+
286
+ // 只顯示在有偏移的標籤
287
+ const filteredData = data.filter(d => d.collisionShiftX || d.collisionShiftY)
288
+
289
+ // 添加標籤的連接線
290
+ const lines = lineGSelection.selectAll<SVGPolylineElement, RenderDatum>("polyline")
291
+ .data(filteredData, d => d.pieDatum.id)
292
+ .join("polyline")
293
+ .attr("stroke", d => getDatumColor({ datum: d.pieDatum.data, colorType: layerParams.labelColorType, theme }))
294
+ .attr("stroke-width", 1)
295
+ .attr("fill", "none")
296
+ .attr("points", (d) => {
297
+ return [[d.lineStartX, d.lineStartY], [d.lineStartX, d.lineStartY]] as any // 畫出從弧線中心到延伸點的線
298
+ })
299
+ lines
300
+ .transition()
301
+ .attr("points", (d) => {
302
+ // const pos = arc.centroid(d) // 起點:弧線的中心點
303
+ // const outerPos = [pos[0] * 2.5, pos[1] * 2.5] // 外部延伸的點(乘以倍率來延長線段)
304
+
305
+ let lineEndX = d.x + d.collisionShiftX
306
+ let lineEndY = d.y + d.collisionShiftY
307
+ // if (d.lineStartX >= Math.abs(d.lineStartY)) {
308
+ // lineEndX -= d.textWidth / 2
309
+ // } else if (d.lineStartX <= - Math.abs(d.lineStartY)) {
310
+ // lineEndX += d.textWidth / 2
311
+ // }
312
+
313
+ return [[lineEndX, lineEndY], [d.lineStartX, d.lineStartY]] as any // 畫出從弧線中心到延伸點的線
314
+ })
315
+
316
+ return lines
317
+ }
318
+
319
+ function highlight ({ textSelection, lineSelection, ids, pluginParams }: {
320
+ textSelection: d3.Selection<SVGTextElement, RenderDatum, any, any>
321
+ lineSelection: d3.Selection<SVGPolylineElement, RenderDatum, any, any>
322
+ ids: string[]
323
+ pluginParams: CompositionPlotPluginParams
324
+ }) {
325
+ textSelection.interrupt('highlight')
326
+ lineSelection.interrupt('highlight')
327
+
328
+ if (!ids.length) {
329
+ textSelection
330
+ .transition('highlight')
331
+ .duration(200)
332
+ .attr('transform', (d) => {
333
+ return `translate(${d.x + d.collisionShiftX},${d.y + d.collisionShiftY})`
334
+ })
335
+ .style('opacity', 1)
336
+ lineSelection
337
+ .transition('highlight')
338
+ .duration(200)
339
+ .style('opacity', 1)
340
+ return
341
+ }
342
+
343
+ textSelection.each((d, i, n) => {
344
+ const segment = d3.select<SVGTextElement, RenderDatum>(n[i])
345
+
346
+ if (ids.includes(d.pieDatum.data.id)) {
347
+ segment
348
+ .style('opacity', 1)
349
+ .transition('highlight')
350
+ .duration(200)
351
+ .attr('transform', (d) => {
352
+ return `translate(${d.mouseoverX + d.collisionShiftX},${d.mouseoverY + d.collisionShiftY})`
353
+ })
354
+ } else {
355
+ segment
356
+ .style('opacity', pluginParams.styles.unhighlightedOpacity)
357
+ .transition('highlight')
358
+ .duration(200)
359
+ .attr('transform', (d) => {
360
+ return `translate(${d.x + d.collisionShiftX},${d.y + d.collisionShiftY})`
361
+ })
362
+ }
363
+ })
364
+ lineSelection.each((d, i, n) => {
365
+ const segment = d3.select<SVGPolylineElement, RenderDatum>(n[i])
366
+
367
+ if (ids.includes(d.pieDatum.data.id)) {
368
+ segment
369
+ .style('opacity', 1)
370
+ .transition('highlight')
371
+ .duration(200)
372
+ } else {
373
+ segment
374
+ .style('opacity', pluginParams.styles.unhighlightedOpacity)
375
+ .transition('highlight')
376
+ .duration(200)
377
+ }
378
+ })
379
+ }
380
+
381
+
382
+ function createEachPieLabel (context: {
383
+ containerSelection: d3.Selection<SVGGElement, any, any, unknown>
384
+ // computedData$: Observable<ComputedDatumSeries[][]>
385
+ visibleComputedSortedData$: Observable<ComputedDatumSeries[][]>
386
+ containerVisibleComputedSortedData$: Observable<ComputedDatumSeries[]>
387
+ // SeriesDataMap$: Observable<Map<string, ComputedDatumSeries[]>>
388
+ layerParams$: Observable<RoseLabelsParams>
389
+ pluginParams$: Observable<CompositionPlotPluginParams>
390
+ theme$: Observable<Theme>
391
+ fontSizePx$: Observable<number>
392
+ seriesHighlight$: Observable<ComputedDatumSeries[]>
393
+ seriesContainerPosition$: Observable<ContainerPosition>
394
+ eventTrigger$: Subject<EventData>
395
+ }) {
396
+ const destroy$ = new Subject()
397
+
398
+ context.containerSelection.selectAll('g').remove()
399
+
400
+ const lineGSelection: d3.Selection<SVGGElement, any, any, unknown> = context.containerSelection.append('g')
401
+ lineGSelection.classed(lineGClassName, true)
402
+ const labelGSelection: d3.Selection<SVGGElement, any, any, unknown> = context.containerSelection.append('g')
403
+ labelGSelection.classed(labelGClassName, true)
404
+
405
+ const textSelection$: Subject<d3.Selection<SVGTextElement, RenderDatum, any, any>> = new Subject()
406
+ const lineSelection$: Subject<d3.Selection<SVGPolylineElement, RenderDatum, any, any>> = new Subject()
407
+ let renderData: RenderDatum[] = []
408
+
409
+ const shorterSideWith$ = context.seriesContainerPosition$.pipe(
410
+ takeUntil(destroy$),
411
+ map(d => d.width < d.height ? d.width : d.height),
412
+ distinctUntilChanged()
413
+ )
414
+
415
+ const maxValue$ = context.visibleComputedSortedData$.pipe(
416
+ map(data => Math.max(...data.flat().map(d => d.value))),
417
+ distinctUntilChanged()
418
+ )
419
+
420
+ const lineStartCentroid$ = context.layerParams$.pipe(
421
+ takeUntil(destroy$),
422
+ map(d => {
423
+ return d.labelCentroid >= pieOuterCentroid
424
+ ? pieOuterCentroid // 當 label在 pie的外側時,線條從 pie的邊緣開始
425
+ : d.labelCentroid // 當 label在 pie的內側時,線條從 label未偏移前的位置開始
426
+
427
+ })
428
+ )
429
+
430
+ combineLatest({
431
+ // layout: context.seriesContainerPosition$,
432
+ shorterSideWith: shorterSideWith$,
433
+ containerVisibleComputedSortedData: context.containerVisibleComputedSortedData$,
434
+ maxValue: maxValue$,
435
+ layerParams: context.layerParams$,
436
+ pluginParams: context.pluginParams$,
437
+ theme: context.theme$,
438
+ fontSizePx: context.fontSizePx$,
439
+ lineStartCentroid: lineStartCentroid$
440
+ }).pipe(
441
+ takeUntil(destroy$),
442
+ switchMap(async (d) => d),
443
+ ).subscribe(data => {
444
+
445
+ const eachAngle = Math.PI * 2 / data.containerVisibleComputedSortedData.length
446
+
447
+ const pieData = data.containerVisibleComputedSortedData.map((d, i) => {
448
+ return {
449
+ id: d.id,
450
+ data: d,
451
+ index: i,
452
+ value: d.value,
453
+ startAngle: eachAngle * i,
454
+ endAngle: eachAngle * (i + 1),
455
+ padAngle: 0,
456
+ // prevValue: lastPieData[i] ? lastPieData[i].value : 0
457
+ }
458
+ })
459
+
460
+ renderData = makeRenderData({
461
+ pieData,
462
+ labelCentroid: data.layerParams.labelCentroid,
463
+ arcScaleType: data.layerParams.arcScaleType,
464
+ maxValue: data.maxValue,
465
+ axisWidth: data.shorterSideWith,
466
+ outerRadius: data.layerParams.outerRadius,
467
+ lineStartCentroid: data.lineStartCentroid,
468
+ layerParams: data.layerParams
469
+ })
470
+
471
+ // 先移除線條,等偏移後再重新繪製
472
+ lineGSelection.selectAll('polyline').remove()
473
+
474
+ const textSelection = renderLabel({
475
+ labelGSelection,
476
+ data: renderData,
477
+ layerParams: data.layerParams,
478
+ pluginParams: data.pluginParams,
479
+ theme: data.theme,
480
+ fontSizePx: data.fontSizePx
481
+ })
482
+
483
+ // 等 label 本身的 transition 結束後再進行碰撞檢測
484
+ setTimeout(() => {
485
+ // 偏移 label
486
+ resolveCollisions(textSelection, renderData, data.fontSizePx)
487
+
488
+ const lineSelection = renderLine({ lineGSelection, data: renderData, layerParams: data.layerParams, theme: data.theme })
489
+
490
+ lineSelection$.next(lineSelection)
491
+
492
+ }, 1000)
493
+
494
+ textSelection$.next(textSelection)
495
+
496
+ })
497
+
498
+ combineLatest({
499
+ textSelection: textSelection$,
500
+ lineSelection: lineSelection$,
501
+ highlight: context.seriesHighlight$.pipe(
502
+ map(data => data.map(d => d.id))
503
+ ),
504
+ pluginParams: context.pluginParams$,
505
+ }).pipe(
506
+ takeUntil(destroy$),
507
+ debounceTime(0)
508
+ ).subscribe(data => {
509
+ highlight({
510
+ textSelection: data.textSelection,
511
+ lineSelection: data.lineSelection,
512
+ ids: data.highlight,
513
+ pluginParams: data.pluginParams,
514
+ })
515
+ })
516
+
517
+ return () => {
518
+ destroy$.next(undefined)
519
+ }
520
+ }
521
+
522
+
523
+ export const RoseLabels = defineSVGLayer<CompositionPlotExtendContext, CompositionPlotPluginParams, RoseLabelsParams>({
524
+ name: layerName,
525
+ defaultParams: DEFAULT_ROSE_LABELS_PARAMS,
526
+ layerIndex: LAYER_INDEX_OF_LABEL,
527
+ initShow: false,
528
+ validator: (params) => {
529
+ const result = validateObject(params, {
530
+ outerRadius: {
531
+ toBeTypes: ['number'],
532
+ },
533
+ labelCentroid: {
534
+ toBeTypes: ['number'],
535
+ },
536
+ labelFn: {
537
+ toBeTypes: ['Function'],
538
+ },
539
+ labelColorType: {
540
+ toBeOption: 'ColorType'
541
+ },
542
+ arcScaleType: {
543
+ toBe: '"area" | "radius"',
544
+ test: (value: any) => value === 'area' || value === 'radius'
545
+ },
546
+ })
547
+ return result
548
+ },
549
+ setup: ({ svgG, pluginParams$, layerParams$, context }) => {
550
+
551
+ const destroy$ = new Subject()
552
+
553
+ context.layout$
554
+ .pipe(
555
+ takeUntil(destroy$)
556
+ )
557
+ .subscribe(layout => {
558
+ d3.select(svgG)
559
+ .attr('transform', `translate(${layout.left}, ${layout.top})`)
560
+ })
561
+
562
+ const { seriesCenterSelection$ } = seriesCenterSelectionObservable({
563
+ selection: d3.select(svgG),
564
+ pluginName,
565
+ layerName,
566
+ visibleComputedSortedData$: context.visibleComputedSortedData$,
567
+ seriesContainerPosition$: context.seriesContainerPosition$
568
+ })
569
+
570
+ const unsubscribeFnArr: (() => void)[] = []
571
+
572
+ seriesCenterSelection$
573
+ .pipe(
574
+ takeUntil(destroy$)
575
+ )
576
+ .subscribe(seriesCenterSelection => {
577
+ // 每次重新計算時,清除之前的訂閱
578
+ unsubscribeFnArr.forEach(fn => fn())
579
+
580
+ seriesCenterSelection.each((d, containerIndex, g) => {
581
+
582
+ const containerSelection = d3.select(g[containerIndex])
583
+
584
+ const containerVisibleComputedSortedData$ = context.visibleComputedSortedData$.pipe(
585
+ takeUntil(destroy$),
586
+ map(data => JSON.parse(JSON.stringify(data[containerIndex] ?? data[0])))
587
+ )
588
+
589
+ const containerPosition$ = context.seriesContainerPosition$.pipe(
590
+ takeUntil(destroy$),
591
+ map(data => JSON.parse(JSON.stringify(data[containerIndex] ?? data[0])))
592
+ )
593
+
594
+ unsubscribeFnArr[containerIndex] = createEachPieLabel({
595
+ containerSelection: containerSelection,
596
+ // computedData$: context.computedData$,
597
+ visibleComputedSortedData$: context.visibleComputedSortedData$,
598
+ containerVisibleComputedSortedData$: containerVisibleComputedSortedData$,
599
+ // SeriesDataMap$: context.SeriesDataMap$,
600
+ layerParams$: layerParams$,
601
+ pluginParams$: pluginParams$,
602
+ theme$: context.theme$,
603
+ fontSizePx$: context.fontSizePx$,
604
+ seriesHighlight$: context.seriesHighlight$,
605
+ seriesContainerPosition$: containerPosition$,
606
+ eventTrigger$: context.eventTrigger$,
607
+ })
608
+
609
+ })
610
+ })
611
+
612
+ return () => {
613
+ destroy$.next(undefined)
614
+ }
615
+ }
616
+ })
File without changes