@orbcharts/plugins-basic 3.0.0-beta.15 → 3.0.0-beta.17

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 (228) hide show
  1. package/LICENSE +200 -200
  2. package/dist/orbcharts-plugins-basic.es.js +11839 -10485
  3. package/dist/orbcharts-plugins-basic.umd.js +231 -171
  4. package/dist/{orbcharts-plugins-basic/src → src}/base/BaseBars.d.ts +3 -3
  5. package/dist/{orbcharts-plugins-basic/src → src}/base/BaseBarsTriangle.d.ts +3 -3
  6. package/dist/{orbcharts-plugins-basic/src → src}/base/BaseDots.d.ts +3 -3
  7. package/dist/{orbcharts-plugins-basic/src → src}/base/BaseLineAreas.d.ts +3 -3
  8. package/dist/{orbcharts-plugins-basic/src → src}/base/BaseLines.d.ts +3 -3
  9. package/dist/src/base/BaseRacingBars.d.ts +23 -0
  10. package/dist/src/base/BaseRacingLabels.d.ts +20 -0
  11. package/dist/src/base/BaseRacingValueLabels.d.ts +20 -0
  12. package/dist/{orbcharts-plugins-basic/src/base/BaseStackedBar.d.ts → src/base/BaseStackedBars.d.ts} +7 -7
  13. package/dist/{orbcharts-plugins-basic/src → src}/base/BaseValueAxis.d.ts +4 -7
  14. package/dist/src/base/BaseXAxis.d.ts +20 -0
  15. package/dist/src/base/BaseYAxis.d.ts +18 -0
  16. package/dist/{orbcharts-plugins-basic/src → src}/grid/defaults.d.ts +3 -3
  17. package/dist/{orbcharts-plugins-basic/src → src}/grid/gridObservables.d.ts +8 -1
  18. package/dist/{orbcharts-plugins-basic/src → src}/grid/index.d.ts +2 -2
  19. package/dist/src/grid/plugins/GroupZoom.d.ts +1 -0
  20. package/dist/src/grid/plugins/StackedBars.d.ts +1 -0
  21. package/dist/src/index.d.ts +7 -1
  22. package/dist/{orbcharts-plugins-basic/src → src}/multiGrid/defaults.d.ts +2 -2
  23. package/dist/{orbcharts-plugins-basic/src → src}/multiGrid/index.d.ts +1 -1
  24. package/dist/src/multiGrid/plugins/MultiStackedBars.d.ts +1 -0
  25. package/dist/src/multiValue/defaults.d.ts +13 -0
  26. package/dist/{orbcharts-plugins-basic/src → src}/multiValue/index.d.ts +5 -1
  27. package/dist/src/multiValue/multiValueObservables.d.ts +56 -0
  28. package/dist/src/multiValue/plugins/RacingBars.d.ts +1 -0
  29. package/dist/src/multiValue/plugins/RacingCounterTexts.d.ts +3 -0
  30. package/dist/src/multiValue/plugins/RacingValueAxis.d.ts +1 -0
  31. package/dist/src/multiValue/plugins/XAxis.d.ts +1 -0
  32. package/dist/src/multiValue/plugins/XYAxes.d.ts +1 -0
  33. package/dist/src/multiValue/plugins/XZoom.d.ts +1 -0
  34. package/dist/{orbcharts-plugins-basic/src → src}/utils/commonUtils.d.ts +1 -0
  35. package/dist/{orbcharts-plugins-basic/src → src}/utils/d3Graphics.d.ts +2 -1
  36. package/lib/core-types.ts +7 -7
  37. package/lib/core.ts +6 -6
  38. package/lib/plugins-basic-types.ts +6 -6
  39. package/package.json +44 -44
  40. package/src/base/BaseBars.ts +765 -765
  41. package/src/base/BaseBarsTriangle.ts +676 -676
  42. package/src/base/BaseDots.ts +464 -464
  43. package/src/base/BaseGroupAxis.ts +691 -679
  44. package/src/base/BaseLegend.ts +684 -684
  45. package/src/base/BaseLineAreas.ts +629 -629
  46. package/src/base/BaseLines.ts +706 -706
  47. package/src/base/BaseRacingBars.ts +552 -0
  48. package/src/base/BaseRacingLabels.ts +396 -0
  49. package/src/base/BaseRacingValueLabels.ts +403 -0
  50. package/src/base/{BaseStackedBar.ts → BaseStackedBars.ts} +782 -782
  51. package/src/base/BaseTooltip.ts +386 -385
  52. package/src/base/BaseValueAxis.ts +600 -583
  53. package/src/base/BaseXAxis.ts +427 -0
  54. package/src/base/BaseYAxis.ts +389 -0
  55. package/src/base/types.ts +2 -2
  56. package/src/const.ts +30 -30
  57. package/src/grid/defaults.ts +213 -250
  58. package/src/grid/gridObservables.ts +612 -554
  59. package/src/grid/index.ts +16 -16
  60. package/src/grid/plugins/Bars.ts +69 -69
  61. package/src/grid/plugins/BarsPN.ts +66 -66
  62. package/src/grid/plugins/BarsTriangle.ts +73 -73
  63. package/src/grid/plugins/Dots.ts +68 -68
  64. package/src/grid/plugins/GridLegend.ts +107 -107
  65. package/src/grid/plugins/GridTooltip.ts +66 -66
  66. package/src/grid/plugins/GroupAux.ts +1120 -1103
  67. package/src/grid/plugins/GroupAxis.ts +73 -97
  68. package/src/grid/plugins/{GridZoom.ts → GroupZoom.ts} +218 -218
  69. package/src/grid/plugins/LineAreas.ts +65 -65
  70. package/src/grid/plugins/Lines.ts +59 -59
  71. package/src/grid/plugins/{StackedBar.ts → StackedBars.ts} +64 -64
  72. package/src/grid/plugins/StackedValueAxis.ts +96 -96
  73. package/src/grid/plugins/ValueAxis.ts +94 -94
  74. package/src/index.ts +6 -6
  75. package/src/multiGrid/defaults.ts +244 -228
  76. package/src/multiGrid/index.ts +14 -14
  77. package/src/multiGrid/multiGridObservables.ts +50 -49
  78. package/src/multiGrid/plugins/MultiBars.ts +108 -108
  79. package/src/multiGrid/plugins/MultiBarsTriangle.ts +114 -114
  80. package/src/multiGrid/plugins/MultiDots.ts +102 -102
  81. package/src/multiGrid/plugins/MultiGridLegend.ts +169 -159
  82. package/src/multiGrid/plugins/MultiGridTooltip.ts +66 -66
  83. package/src/multiGrid/plugins/MultiGroupAxis.ts +137 -137
  84. package/src/multiGrid/plugins/MultiLineAreas.ts +107 -107
  85. package/src/multiGrid/plugins/MultiLines.ts +101 -101
  86. package/src/multiGrid/plugins/{MultiStackedBar.ts → MultiStackedBars.ts} +106 -106
  87. package/src/multiGrid/plugins/MultiStackedValueAxis.ts +134 -134
  88. package/src/multiGrid/plugins/MultiValueAxis.ts +134 -134
  89. package/src/multiGrid/plugins/OverlappingStackedValueAxes.ts +300 -299
  90. package/src/multiGrid/plugins/OverlappingValueAxes.ts +300 -300
  91. package/src/multiValue/defaults.ts +388 -166
  92. package/src/multiValue/index.ts +13 -9
  93. package/src/multiValue/multiValueObservables.ts +667 -297
  94. package/src/multiValue/plugins/MultiValueLegend.ts +107 -107
  95. package/src/multiValue/plugins/MultiValueTooltip.ts +66 -66
  96. package/src/multiValue/plugins/OrdinalBubbles.ts +0 -0
  97. package/src/multiValue/plugins/OrdinalXAxis.ts +0 -0
  98. package/src/multiValue/plugins/RacingBars.ts +373 -0
  99. package/src/multiValue/plugins/RacingCounterAxis.ts +0 -0
  100. package/src/multiValue/plugins/RacingCounterTexts.ts +300 -0
  101. package/src/multiValue/plugins/RacingValueAxis.ts +115 -0
  102. package/src/multiValue/plugins/RankingAxis_legacy.ts +109 -0
  103. package/src/multiValue/plugins/Scatter.ts +426 -426
  104. package/src/multiValue/plugins/ScatterBubbles.ts +554 -554
  105. package/src/multiValue/plugins/XAxis.ts +108 -0
  106. package/src/multiValue/plugins/XYAux.ts +682 -681
  107. package/src/multiValue/plugins/XYAxes.ts +194 -684
  108. package/src/multiValue/plugins/XYAxes_legacy.ts +684 -0
  109. package/src/multiValue/plugins/{XYZoom.ts → XZoom.ts} +299 -299
  110. package/src/noneData/defaults.ts +102 -102
  111. package/src/noneData/index.ts +3 -3
  112. package/src/noneData/plugins/Container.ts +27 -27
  113. package/src/noneData/plugins/Tooltip.ts +373 -373
  114. package/src/relationship/defaults.ts +220 -218
  115. package/src/relationship/index.ts +5 -5
  116. package/src/relationship/plugins/ForceDirected.ts +1168 -1168
  117. package/src/relationship/plugins/ForceDirectedBubbles.ts +1403 -1403
  118. package/src/relationship/plugins/RelationshipLegend.ts +100 -100
  119. package/src/relationship/plugins/RelationshipTooltip.ts +66 -66
  120. package/src/relationship/relationshipObservables.ts +49 -49
  121. package/src/series/defaults.ts +221 -230
  122. package/src/series/index.ts +9 -9
  123. package/src/series/plugins/Bubbles.ts +636 -620
  124. package/src/series/plugins/Pie.ts +623 -623
  125. package/src/series/plugins/PieEventTexts.ts +284 -284
  126. package/src/series/plugins/PieLabels.ts +640 -640
  127. package/src/series/plugins/Rose.ts +516 -516
  128. package/src/series/plugins/RoseLabels.ts +600 -600
  129. package/src/series/plugins/SeriesLegend.ts +107 -107
  130. package/src/series/plugins/SeriesTooltip.ts +66 -66
  131. package/src/series/seriesObservables.ts +145 -145
  132. package/src/series/seriesUtils.ts +51 -51
  133. package/src/tree/defaults.ts +102 -100
  134. package/src/tree/index.ts +4 -4
  135. package/src/tree/plugins/TreeLegend.ts +100 -100
  136. package/src/tree/plugins/TreeMap.ts +341 -341
  137. package/src/tree/plugins/TreeTooltip.ts +66 -66
  138. package/src/utils/commonUtils.ts +31 -22
  139. package/src/utils/d3Graphics.ts +176 -174
  140. package/src/utils/d3Utils.ts +92 -92
  141. package/src/utils/observables.ts +14 -14
  142. package/src/utils/orbchartsUtils.ts +129 -129
  143. package/tsconfig.base.json +13 -13
  144. package/tsconfig.json +2 -2
  145. package/vite.config.js +22 -22
  146. package/dist/orbcharts-plugins-basic/src/grid/plugins/GridZoom.d.ts +0 -1
  147. package/dist/orbcharts-plugins-basic/src/grid/plugins/StackedBar.d.ts +0 -1
  148. package/dist/orbcharts-plugins-basic/src/index.d.ts +0 -7
  149. package/dist/orbcharts-plugins-basic/src/multiGrid/plugins/MultiStackedBar.d.ts +0 -1
  150. package/dist/orbcharts-plugins-basic/src/multiValue/defaults.d.ts +0 -9
  151. package/dist/orbcharts-plugins-basic/src/multiValue/multiValueObservables.d.ts +0 -33
  152. package/dist/orbcharts-plugins-basic/src/multiValue/plugins/XYZoom.d.ts +0 -1
  153. /package/dist/{orbcharts-plugins-basic/lib → lib}/core-types.d.ts +0 -0
  154. /package/dist/{orbcharts-plugins-basic/lib → lib}/core.d.ts +0 -0
  155. /package/dist/{orbcharts-plugins-basic/lib → lib}/plugins-basic-types.d.ts +0 -0
  156. /package/dist/{orbcharts-plugins-basic/src → src}/base/BaseGroupAxis.d.ts +0 -0
  157. /package/dist/{orbcharts-plugins-basic/src → src}/base/BaseLegend.d.ts +0 -0
  158. /package/dist/{orbcharts-plugins-basic/src → src}/base/BaseTooltip.d.ts +0 -0
  159. /package/dist/{orbcharts-plugins-basic/src → src}/base/types.d.ts +0 -0
  160. /package/dist/{orbcharts-plugins-basic/src → src}/const.d.ts +0 -0
  161. /package/dist/{orbcharts-plugins-basic/src → src}/grid/plugins/Bars.d.ts +0 -0
  162. /package/dist/{orbcharts-plugins-basic/src → src}/grid/plugins/BarsPN.d.ts +0 -0
  163. /package/dist/{orbcharts-plugins-basic/src → src}/grid/plugins/BarsTriangle.d.ts +0 -0
  164. /package/dist/{orbcharts-plugins-basic/src → src}/grid/plugins/Dots.d.ts +0 -0
  165. /package/dist/{orbcharts-plugins-basic/src → src}/grid/plugins/GridLegend.d.ts +0 -0
  166. /package/dist/{orbcharts-plugins-basic/src → src}/grid/plugins/GridTooltip.d.ts +0 -0
  167. /package/dist/{orbcharts-plugins-basic/src → src}/grid/plugins/GroupAux.d.ts +0 -0
  168. /package/dist/{orbcharts-plugins-basic/src → src}/grid/plugins/GroupAxis.d.ts +0 -0
  169. /package/dist/{orbcharts-plugins-basic/src → src}/grid/plugins/LineAreas.d.ts +0 -0
  170. /package/dist/{orbcharts-plugins-basic/src → src}/grid/plugins/Lines.d.ts +0 -0
  171. /package/dist/{orbcharts-plugins-basic/src → src}/grid/plugins/StackedValueAxis.d.ts +0 -0
  172. /package/dist/{orbcharts-plugins-basic/src → src}/grid/plugins/ValueAxis.d.ts +0 -0
  173. /package/dist/{orbcharts-plugins-basic/src → src}/multiGrid/multiGridObservables.d.ts +0 -0
  174. /package/dist/{orbcharts-plugins-basic/src → src}/multiGrid/plugins/MultiBars.d.ts +0 -0
  175. /package/dist/{orbcharts-plugins-basic/src → src}/multiGrid/plugins/MultiBarsTriangle.d.ts +0 -0
  176. /package/dist/{orbcharts-plugins-basic/src → src}/multiGrid/plugins/MultiDots.d.ts +0 -0
  177. /package/dist/{orbcharts-plugins-basic/src → src}/multiGrid/plugins/MultiGridLegend.d.ts +0 -0
  178. /package/dist/{orbcharts-plugins-basic/src → src}/multiGrid/plugins/MultiGridTooltip.d.ts +0 -0
  179. /package/dist/{orbcharts-plugins-basic/src → src}/multiGrid/plugins/MultiGroupAxis.d.ts +0 -0
  180. /package/dist/{orbcharts-plugins-basic/src → src}/multiGrid/plugins/MultiLineAreas.d.ts +0 -0
  181. /package/dist/{orbcharts-plugins-basic/src → src}/multiGrid/plugins/MultiLines.d.ts +0 -0
  182. /package/dist/{orbcharts-plugins-basic/src → src}/multiGrid/plugins/MultiStackedValueAxis.d.ts +0 -0
  183. /package/dist/{orbcharts-plugins-basic/src → src}/multiGrid/plugins/MultiValueAxis.d.ts +0 -0
  184. /package/dist/{orbcharts-plugins-basic/src → src}/multiGrid/plugins/OverlappingStackedValueAxes.d.ts +0 -0
  185. /package/dist/{orbcharts-plugins-basic/src → src}/multiGrid/plugins/OverlappingValueAxes.d.ts +0 -0
  186. /package/dist/{orbcharts-plugins-basic/src → src}/multiValue/plugins/MultiValueLegend.d.ts +0 -0
  187. /package/dist/{orbcharts-plugins-basic/src → src}/multiValue/plugins/MultiValueTooltip.d.ts +0 -0
  188. /package/dist/{orbcharts-plugins-basic/src/multiValue/plugins/Ranking.d.ts → src/multiValue/plugins/OrdinalBubbles.d.ts} +0 -0
  189. /package/dist/{orbcharts-plugins-basic/src/multiValue/plugins/RankingAxis.d.ts → src/multiValue/plugins/OrdinalXAxis.d.ts} +0 -0
  190. /package/dist/{orbcharts-plugins-basic/src/noneData/plugins/Container.d.ts → src/multiValue/plugins/RacingCounterAxis.d.ts} +0 -0
  191. /package/dist/{orbcharts-plugins-basic/src/noneData/plugins/Tooltip.d.ts → src/multiValue/plugins/RankingAxis_legacy.d.ts} +0 -0
  192. /package/dist/{orbcharts-plugins-basic/src → src}/multiValue/plugins/Scatter.d.ts +0 -0
  193. /package/dist/{orbcharts-plugins-basic/src → src}/multiValue/plugins/ScatterBubbles.d.ts +0 -0
  194. /package/dist/{orbcharts-plugins-basic/src → src}/multiValue/plugins/XYAux.d.ts +0 -0
  195. /package/dist/{orbcharts-plugins-basic/src/multiValue/plugins/XYAxes.d.ts → src/multiValue/plugins/XYAxes_legacy.d.ts} +0 -0
  196. /package/dist/{orbcharts-plugins-basic/src → src}/noneData/defaults.d.ts +0 -0
  197. /package/dist/{orbcharts-plugins-basic/src → src}/noneData/index.d.ts +0 -0
  198. /package/dist/{orbcharts-plugins-basic/src/series/plugins/Waffle.d.ts → src/noneData/plugins/Container.d.ts} +0 -0
  199. /package/{src/multiValue/plugins/Ranking.ts → dist/src/noneData/plugins/Tooltip.d.ts} +0 -0
  200. /package/dist/{orbcharts-plugins-basic/src → src}/relationship/defaults.d.ts +0 -0
  201. /package/dist/{orbcharts-plugins-basic/src → src}/relationship/index.d.ts +0 -0
  202. /package/dist/{orbcharts-plugins-basic/src → src}/relationship/plugins/ForceDirected.d.ts +0 -0
  203. /package/dist/{orbcharts-plugins-basic/src → src}/relationship/plugins/ForceDirectedBubbles.d.ts +0 -0
  204. /package/dist/{orbcharts-plugins-basic/src → src}/relationship/plugins/RelationshipLegend.d.ts +0 -0
  205. /package/dist/{orbcharts-plugins-basic/src → src}/relationship/plugins/RelationshipTooltip.d.ts +0 -0
  206. /package/dist/{orbcharts-plugins-basic/src → src}/relationship/relationshipObservables.d.ts +0 -0
  207. /package/dist/{orbcharts-plugins-basic/src → src}/series/defaults.d.ts +0 -0
  208. /package/dist/{orbcharts-plugins-basic/src → src}/series/index.d.ts +0 -0
  209. /package/dist/{orbcharts-plugins-basic/src → src}/series/plugins/Bubbles.d.ts +0 -0
  210. /package/dist/{orbcharts-plugins-basic/src → src}/series/plugins/Pie.d.ts +0 -0
  211. /package/dist/{orbcharts-plugins-basic/src → src}/series/plugins/PieEventTexts.d.ts +0 -0
  212. /package/dist/{orbcharts-plugins-basic/src → src}/series/plugins/PieLabels.d.ts +0 -0
  213. /package/dist/{orbcharts-plugins-basic/src → src}/series/plugins/Rose.d.ts +0 -0
  214. /package/dist/{orbcharts-plugins-basic/src → src}/series/plugins/RoseLabels.d.ts +0 -0
  215. /package/dist/{orbcharts-plugins-basic/src → src}/series/plugins/SeriesLegend.d.ts +0 -0
  216. /package/dist/{orbcharts-plugins-basic/src → src}/series/plugins/SeriesTooltip.d.ts +0 -0
  217. /package/{src/multiValue/plugins/RankingAxis.ts → dist/src/series/plugins/Waffle.d.ts} +0 -0
  218. /package/dist/{orbcharts-plugins-basic/src → src}/series/seriesObservables.d.ts +0 -0
  219. /package/dist/{orbcharts-plugins-basic/src → src}/series/seriesUtils.d.ts +0 -0
  220. /package/dist/{orbcharts-plugins-basic/src → src}/tree/defaults.d.ts +0 -0
  221. /package/dist/{orbcharts-plugins-basic/src → src}/tree/index.d.ts +0 -0
  222. /package/dist/{orbcharts-plugins-basic/src → src}/tree/plugins/TreeLegend.d.ts +0 -0
  223. /package/dist/{orbcharts-plugins-basic/src → src}/tree/plugins/TreeMap.d.ts +0 -0
  224. /package/dist/{orbcharts-plugins-basic/src → src}/tree/plugins/TreeTooltip.d.ts +0 -0
  225. /package/dist/{orbcharts-plugins-basic/src → src}/utils/d3Utils.d.ts +0 -0
  226. /package/dist/{orbcharts-plugins-basic/src → src}/utils/observables.d.ts +0 -0
  227. /package/dist/{orbcharts-plugins-basic/src → src}/utils/orbchartsUtils.d.ts +0 -0
  228. /package/dist/{orbcharts-plugins-basic/vite.config.d.ts → vite.config.d.ts} +0 -0
@@ -1,297 +1,667 @@
1
- import * as d3 from 'd3'
2
- import {
3
- Observable,
4
- Subject,
5
- debounceTime,
6
- of,
7
- takeUntil,
8
- filter,
9
- map,
10
- switchMap,
11
- combineLatest,
12
- merge,
13
- shareReplay,
14
- distinctUntilChanged
15
- } from 'rxjs'
16
- import type {
17
- ChartParams,
18
- HighlightTarget,
19
- DataFormatterMultiValue,
20
- ComputedDataMultiValue,
21
- ComputedDatumMultiValue,
22
- ComputedLayoutDatumMultiValue,
23
- TransformData,
24
- ContainerPositionScaled,
25
- Layout,
26
- } from '../../lib/core-types'
27
- import { createAxisToLabelIndexScale, createAxisToValueScale } from '../../lib/core'
28
- import { getClassName, getUniID } from '../utils/orbchartsUtils'
29
- import { d3EventObservable } from '../utils/observables'
30
-
31
- // 建立 multiValue 主要的 selection
32
- export const multiValueSelectionsObservable = ({ selection, pluginName, clipPathID, categoryLabels$, multiValueContainerPosition$, multiValueGraphicTransform$ }: {
33
- selection: d3.Selection<any, unknown, any, unknown>
34
- pluginName: string
35
- clipPathID: string
36
- // computedData$: Observable<ComputedDataMultiValue>
37
- categoryLabels$: Observable<string[]>
38
- multiValueContainerPosition$: Observable<ContainerPositionScaled[]>
39
- // multiValueAxesTransform$: Observable<TransformData>
40
- multiValueGraphicTransform$: Observable<TransformData>
41
- }) => {
42
- const categoryClassName = getClassName(pluginName, 'category')
43
- const axesClassName = getClassName(pluginName, 'axes')
44
- const graphicClassName = getClassName(pluginName, 'graphic')
45
-
46
- // <g> category selection(container排放位置)
47
- // <g> axes selection(圖軸)
48
- // <defs> clipPath selection
49
- // <g> graphic selection(圖形 scale 範圍的變形)
50
- const categorySelection$ = categoryLabels$.pipe(
51
- map((categoryLabels, i) => {
52
- return selection
53
- .selectAll<SVGGElement, string>(`g.${categoryClassName}`)
54
- .data(categoryLabels, d => d)
55
- .join(
56
- enter => {
57
- return enter
58
- .append('g')
59
- .classed(categoryClassName, true)
60
- .each((d, i, g) => {
61
- const axesSelection = d3.select(g[i])
62
- .selectAll<SVGGElement, ComputedDatumMultiValue[]>(`g.${axesClassName}`)
63
- .data([i])
64
- .join(
65
- enter => {
66
- return enter
67
- .append('g')
68
- .classed(axesClassName, true)
69
- .attr('clip-path', `url(#${clipPathID})`)
70
- .each((d, i, g) => {
71
- const defsSelection = d3.select(g[i])
72
- .selectAll<SVGDefsElement, any>('defs')
73
- .data([i])
74
- .join('defs')
75
-
76
- const graphicGSelection = d3.select(g[i])
77
- .selectAll<SVGGElement, any>('g')
78
- .data([i])
79
- .join('g')
80
- .classed(graphicClassName, true)
81
- })
82
- },
83
- update => update,
84
- exit => exit.remove()
85
- )
86
- })
87
- },
88
- update => update,
89
- exit => exit.remove()
90
- )
91
- }),
92
- shareReplay(1)
93
- )
94
-
95
- // <g> category selection
96
- combineLatest({
97
- categorySelection: categorySelection$,
98
- multiValueContainerPosition: multiValueContainerPosition$
99
- }).pipe(
100
- switchMap(async d => d)
101
- ).subscribe(data => {
102
- data.categorySelection
103
- .transition()
104
- .attr('transform', (d, i) => {
105
- const multiValueContainerPosition = data.multiValueContainerPosition[i] ?? data.multiValueContainerPosition[0]
106
- const translate = multiValueContainerPosition.translate
107
- const scale = multiValueContainerPosition.scale
108
- return `translate(${translate[0]}, ${translate[1]}) scale(${scale[0]}, ${scale[1]})`
109
- })
110
- })
111
-
112
- // <g> axes selection
113
- const axesSelection$ = categorySelection$.pipe(
114
- map(categorySelection => {
115
- return categorySelection
116
- .select<SVGGElement>(`g.${axesClassName}`)
117
- }),
118
- shareReplay(1)
119
- )
120
-
121
- // <defs> clipPath selection
122
- const defsSelection$ = axesSelection$.pipe(
123
- map(axesSelection => {
124
- return axesSelection.select<SVGDefsElement>('defs')
125
- }),
126
- shareReplay(1)
127
- )
128
-
129
- // <g> graphic selection
130
- const graphicGSelection$ = combineLatest({
131
- axesSelection: axesSelection$,
132
- multiValueGraphicTransform: multiValueGraphicTransform$
133
- }).pipe(
134
- switchMap(async d => d),
135
- map(data => {
136
- const graphicGSelection = data.axesSelection
137
- .select<SVGGElement>(`g.${graphicClassName}`)
138
- graphicGSelection
139
- .transition()
140
- .duration(50)
141
- .style('transform', data.multiValueGraphicTransform.value)
142
- return graphicGSelection
143
- }),
144
- shareReplay(1)
145
- )
146
-
147
- return {
148
- categorySelection$,
149
- axesSelection$,
150
- defsSelection$,
151
- graphicGSelection$
152
- }
153
- }
154
-
155
-
156
- export const multiValueXYPositionObservable = ({ rootSelection, fullDataFormatter$, filteredMinMaxXYData$, multiValueContainerPosition$, layout$ }: {
157
- rootSelection: d3.Selection<any, unknown, any, unknown>
158
- fullDataFormatter$: Observable<DataFormatterMultiValue>
159
- // computedData$: Observable<ComputedDataMultiValue>
160
- // minMaxXY$: Observable<{ minX: number, maxX: number, minY: number, maxY: number }>
161
- filteredMinMaxXYData$: Observable<{
162
- minXDatum: ComputedLayoutDatumMultiValue
163
- maxXDatum: ComputedLayoutDatumMultiValue
164
- minYDatum: ComputedLayoutDatumMultiValue
165
- maxYDatum: ComputedLayoutDatumMultiValue
166
- }>
167
- multiValueContainerPosition$: Observable<ContainerPositionScaled[]>
168
- layout$: Observable<Layout>
169
- }) => {
170
- const rootMousemove$ = d3EventObservable(rootSelection, 'mousemove').pipe(
171
- debounceTime(2) // 避免過度頻繁觸發,實測時沒加電腦容易卡頓
172
- )
173
-
174
- const columnAmount$ = multiValueContainerPosition$.pipe(
175
- map(multiValueContainerPosition => {
176
- const maxColumnIndex = multiValueContainerPosition.reduce((acc, current) => {
177
- return current.columnIndex > acc ? current.columnIndex : acc
178
- }, 0)
179
- return maxColumnIndex + 1
180
- }),
181
- distinctUntilChanged(),
182
- shareReplay(1)
183
- )
184
-
185
- const rowAmount$ = multiValueContainerPosition$.pipe(
186
- map(multiValueContainerPosition => {
187
- const maxRowIndex = multiValueContainerPosition.reduce((acc, current) => {
188
- return current.rowIndex > acc ? current.rowIndex : acc
189
- }, 0)
190
- return maxRowIndex + 1
191
- }),
192
- distinctUntilChanged(),
193
- shareReplay(1)
194
- )
195
-
196
- // const xyScale$ = combineLatest({
197
- // layout: layout$,
198
- // filteredMinMaxXYData: filteredMinMaxXYData$,
199
- // fullDataFormatter: fullDataFormatter$,
200
- // columnAmount: columnAmount$,
201
- // rowAmount: rowAmount$
202
- // }).pipe(
203
- // switchMap(async d => d),
204
- // map(data => {
205
- // const xScale = createAxisToValueScale({
206
- // maxValue: data.filteredMinMaxXYData.maxXDatum.value[0],
207
- // minValue: data.filteredMinMaxXYData.minXDatum.value[0],
208
- // axisWidth: data.layout.width,
209
- // scaleDomain: data.fullDataFormatter.xAxis.scaleDomain,
210
- // scaleRange: data.fullDataFormatter.xAxis.scaleRange,
211
- // })
212
- // const yScale = createAxisToValueScale({
213
- // maxValue: data.filteredMinMaxXYData.maxYDatum.value[1],
214
- // minValue: data.filteredMinMaxXYData.minYDatum.value[1],
215
- // axisWidth: data.layout.height,
216
- // scaleDomain: data.fullDataFormatter.yAxis.scaleDomain,
217
- // scaleRange: data.fullDataFormatter.yAxis.scaleRange,
218
- // reverse: true
219
- // })
220
- // return { xScale, yScale }
221
- // })
222
- // )
223
-
224
- const xyScale$: Observable<{
225
- xScale: d3.ScaleLinear<number, number, never>;
226
- yScale: d3.ScaleLinear<number, number, never>;
227
- }> = new Observable(subscriber => {
228
- combineLatest({
229
- layout: layout$,
230
- filteredMinMaxXYData: filteredMinMaxXYData$,
231
- fullDataFormatter: fullDataFormatter$,
232
- columnAmount: columnAmount$,
233
- rowAmount: rowAmount$
234
- }).pipe(
235
- switchMap(async d => d),
236
- ).subscribe(data => {
237
- if (!data.filteredMinMaxXYData.minXDatum || !data.filteredMinMaxXYData.maxXDatum
238
- || data.filteredMinMaxXYData.minXDatum.value[0] == null || data.filteredMinMaxXYData.maxXDatum.value[0] == null
239
- || !data.filteredMinMaxXYData.minYDatum || !data.filteredMinMaxXYData.maxYDatum
240
- || data.filteredMinMaxXYData.minYDatum.value[1] == null || data.filteredMinMaxXYData.maxYDatum.value[1] == null
241
- ) {
242
- return
243
- }
244
- const xScale = createAxisToValueScale({
245
- maxValue: data.filteredMinMaxXYData.maxXDatum.value[0],
246
- minValue: data.filteredMinMaxXYData.minXDatum.value[0],
247
- axisWidth: data.layout.width,
248
- scaleDomain: data.fullDataFormatter.xAxis.scaleDomain,
249
- scaleRange: data.fullDataFormatter.xAxis.scaleRange,
250
- })
251
- const yScale = createAxisToValueScale({
252
- maxValue: data.filteredMinMaxXYData.maxYDatum.value[1],
253
- minValue: data.filteredMinMaxXYData.minYDatum.value[1],
254
- axisWidth: data.layout.height,
255
- scaleDomain: data.fullDataFormatter.yAxis.scaleDomain,
256
- scaleRange: data.fullDataFormatter.yAxis.scaleRange,
257
- reverse: true
258
- })
259
- subscriber.next({ xScale, yScale })
260
- })
261
- })
262
-
263
- const axisValue$ = combineLatest({
264
- fullDataFormatter: fullDataFormatter$,
265
- rootMousemove: rootMousemove$,
266
- columnAmount: columnAmount$,
267
- rowAmount: rowAmount$,
268
- layout: layout$,
269
- multiValueContainerPosition: multiValueContainerPosition$
270
- }).pipe(
271
- switchMap(async d => d),
272
- map(data => {
273
- // 由於event座標是基於底層的,但是container會有多欄,所以要重新計算
274
- return {
275
- x: ((data.rootMousemove.offsetX - data.layout.left) / data.multiValueContainerPosition[0].scale[0])
276
- % (data.layout.rootWidth / data.columnAmount / data.multiValueContainerPosition[0].scale[0]),
277
- y: ((data.rootMousemove.offsetY - data.layout.top) / data.multiValueContainerPosition[0].scale[1])
278
- % (data.layout.rootHeight / data.rowAmount / data.multiValueContainerPosition[0].scale[1])
279
- }
280
- })
281
- )
282
-
283
- return combineLatest({
284
- xyScale: xyScale$,
285
- axisValue: axisValue$
286
- }).pipe(
287
- switchMap(async d => d),
288
- map(data => {
289
- return {
290
- x: data.axisValue.x,
291
- y: data.axisValue.y,
292
- xValue: data.xyScale.xScale(data.axisValue.x),
293
- yValue: data.xyScale.yScale(data.axisValue.y)
294
- }
295
- })
296
- )
297
- }
1
+ import * as d3 from 'd3'
2
+ import {
3
+ Observable,
4
+ Subject,
5
+ debounceTime,
6
+ iif,
7
+ of,
8
+ takeUntil,
9
+ filter,
10
+ map,
11
+ switchMap,
12
+ combineLatest,
13
+ merge,
14
+ shareReplay,
15
+ distinctUntilChanged
16
+ } from 'rxjs'
17
+ import type {
18
+ ChartParams,
19
+ HighlightTarget,
20
+ DataFormatterMultiValue,
21
+ ComputedDataMultiValue,
22
+ ComputedDatumMultiValue,
23
+ ComputedXYDatumMultiValue,
24
+ ComputedXYDataMultiValue,
25
+ TransformData,
26
+ ContainerSize,
27
+ ContainerPositionScaled,
28
+ Layout,
29
+ } from '../../lib/core-types'
30
+ import {
31
+ createAxisToLabelIndexScale,
32
+ createAxisToValueScale,
33
+ createLabelToAxisScale,
34
+ createValueToAxisScale,
35
+ } from '../../lib/core'
36
+ import { getClassName, getUniID } from '../utils/orbchartsUtils'
37
+ import { d3EventObservable } from '../utils/observables'
38
+
39
+ // 建立 multiValue 主要的 selection
40
+ export const multiValueSelectionsObservable = ({ selection, pluginName, clipPathID, categoryLabels$, containerPosition$, graphicTransform$ }: {
41
+ selection: d3.Selection<any, unknown, any, unknown>
42
+ pluginName: string
43
+ clipPathID: string
44
+ // computedData$: Observable<ComputedDataMultiValue>
45
+ categoryLabels$: Observable<string[]>
46
+ containerPosition$: Observable<ContainerPositionScaled[]>
47
+ // multiValueAxesTransform$: Observable<TransformData>
48
+ graphicTransform$: Observable<TransformData>
49
+ }) => {
50
+ const categoryClassName = getClassName(pluginName, 'category')
51
+ const axesClassName = getClassName(pluginName, 'axes')
52
+ const graphicClassName = getClassName(pluginName, 'graphic')
53
+
54
+ // <g> category selection(container排放位置)
55
+ // <g> axes selection(圖軸)
56
+ // <defs> clipPath selection
57
+ // <g> graphic selection(圖形 scale 範圍的變形)
58
+ const categorySelection$ = categoryLabels$.pipe(
59
+ map((categoryLabels, i) => {
60
+ return selection
61
+ .selectAll<SVGGElement, string>(`g.${categoryClassName}`)
62
+ .data(categoryLabels, d => d)
63
+ .join(
64
+ enter => {
65
+ return enter
66
+ .append('g')
67
+ .classed(categoryClassName, true)
68
+ .each((d, i, g) => {
69
+ const axesSelection = d3.select(g[i])
70
+ .selectAll<SVGGElement, ComputedDatumMultiValue[]>(`g.${axesClassName}`)
71
+ .data([i])
72
+ .join(
73
+ enter => {
74
+ return enter
75
+ .append('g')
76
+ .classed(axesClassName, true)
77
+ .attr('clip-path', `url(#${clipPathID})`)
78
+ .each((d, i, g) => {
79
+ const defsSelection = d3.select(g[i])
80
+ .selectAll<SVGDefsElement, any>('defs')
81
+ .data([i])
82
+ .join('defs')
83
+
84
+ const graphicGSelection = d3.select(g[i])
85
+ .selectAll<SVGGElement, any>('g')
86
+ .data([i])
87
+ .join('g')
88
+ .classed(graphicClassName, true)
89
+ })
90
+ },
91
+ update => update,
92
+ exit => exit.remove()
93
+ )
94
+ })
95
+ },
96
+ update => update,
97
+ exit => exit.remove()
98
+ )
99
+ }),
100
+ shareReplay(1)
101
+ )
102
+
103
+ // <g> category selection
104
+ combineLatest({
105
+ categorySelection: categorySelection$,
106
+ containerPosition: containerPosition$
107
+ }).pipe(
108
+ switchMap(async d => d)
109
+ ).subscribe(data => {
110
+ data.categorySelection
111
+ .transition()
112
+ .attr('transform', (d, i) => {
113
+ const containerPosition = data.containerPosition[i] ?? data.containerPosition[0]
114
+ const translate = containerPosition.translate
115
+ const scale = containerPosition.scale
116
+ return `translate(${translate[0]}, ${translate[1]}) scale(${scale[0]}, ${scale[1]})`
117
+ })
118
+ })
119
+
120
+ // <g> axes selection
121
+ const axesSelection$ = categorySelection$.pipe(
122
+ map(categorySelection => {
123
+ return categorySelection
124
+ .select<SVGGElement>(`g.${axesClassName}`)
125
+ }),
126
+ shareReplay(1)
127
+ )
128
+
129
+ // <defs> clipPath selection
130
+ const defsSelection$ = axesSelection$.pipe(
131
+ map(axesSelection => {
132
+ return axesSelection.select<SVGDefsElement>('defs')
133
+ }),
134
+ shareReplay(1)
135
+ )
136
+
137
+ // <g> graphic selection
138
+ const graphicGSelection$ = combineLatest({
139
+ axesSelection: axesSelection$,
140
+ graphicTransform: graphicTransform$
141
+ }).pipe(
142
+ switchMap(async d => d),
143
+ map(data => {
144
+ const graphicGSelection = data.axesSelection
145
+ .select<SVGGElement>(`g.${graphicClassName}`)
146
+ graphicGSelection
147
+ .transition()
148
+ .duration(50)
149
+ .style('transform', data.graphicTransform.value)
150
+ return graphicGSelection
151
+ }),
152
+ shareReplay(1)
153
+ )
154
+
155
+ return {
156
+ categorySelection$,
157
+ axesSelection$,
158
+ defsSelection$,
159
+ graphicGSelection$
160
+ }
161
+ }
162
+
163
+ // 建立 multiValue 主要的 selection - 只取無scale的container selection
164
+ export const multiValueContainerSelectionsObservable = ({ selection, pluginName, clipPathID, computedData$, containerPosition$, isCategorySeprate$ }: {
165
+ selection: d3.Selection<any, unknown, any, unknown>
166
+ pluginName: string
167
+ clipPathID: string | null
168
+ computedData$: Observable<ComputedDataMultiValue>
169
+ containerPosition$: Observable<ContainerPositionScaled[]>
170
+ isCategorySeprate$: Observable<boolean>
171
+ }) => {
172
+ const containerClassName = getClassName(pluginName, 'container')
173
+
174
+ const containerSelection$ = combineLatest({
175
+ computedData: computedData$.pipe(
176
+ distinctUntilChanged((a, b) => {
177
+ // 只有當series的數量改變時,才重新計算
178
+ return a.length === b.length
179
+ }),
180
+ ),
181
+ isCategorySeprate: isCategorySeprate$
182
+ }).pipe(
183
+ switchMap(async (d) => d),
184
+ map(data => {
185
+ return data.isCategorySeprate
186
+ // category分開的時候顯示各別axis
187
+ ? data.computedData
188
+ // category合併的時候只顯示第一個axis
189
+ : [data.computedData[0]]
190
+ }),
191
+ map((computedData, i) => {
192
+ return selection
193
+ .selectAll<SVGGElement, ComputedDatumMultiValue[]>(`g.${containerClassName}`)
194
+ .data(computedData, d => d[0] ? d[0].categoryIndex : i)
195
+ .join('g')
196
+ .classed(containerClassName, true)
197
+ .attr('clip-path', _ => clipPathID ? `url(#${clipPathID})` : 'none')
198
+ }),
199
+ shareReplay(1)
200
+ )
201
+
202
+ combineLatest({
203
+ containerSelection: containerSelection$,
204
+ gridContainerPosition: containerPosition$
205
+ }).pipe(
206
+ switchMap(async d => d)
207
+ ).subscribe(data => {
208
+ data.containerSelection
209
+ .attr('transform', (d, i) => {
210
+ const gridContainerPosition = data.gridContainerPosition[i] ?? data.gridContainerPosition[0]
211
+ const translate = gridContainerPosition.translate
212
+ const scale = gridContainerPosition.scale
213
+ // return `translate(${translate[0]}, ${translate[1]}) scale(${scale[0]}, ${scale[1]})`
214
+ return `translate(${translate[0]}, ${translate[1]})`
215
+ })
216
+ // .attr('opacity', 0)
217
+ // .transition()
218
+ // .attr('opacity', 1)
219
+ })
220
+
221
+ return containerSelection$
222
+ }
223
+
224
+
225
+ export const multiValueXYPositionObservable = ({ rootSelection, fullDataFormatter$, filteredXYMinMaxData$, containerPosition$, layout$ }: {
226
+ rootSelection: d3.Selection<any, unknown, any, unknown>
227
+ fullDataFormatter$: Observable<DataFormatterMultiValue>
228
+ // computedData$: Observable<ComputedDataMultiValue>
229
+ // xyMinMax$: Observable<{ minX: number, maxX: number, minY: number, maxY: number }>
230
+ filteredXYMinMaxData$: Observable<{
231
+ minXDatum: ComputedXYDatumMultiValue
232
+ maxXDatum: ComputedXYDatumMultiValue
233
+ minYDatum: ComputedXYDatumMultiValue
234
+ maxYDatum: ComputedXYDatumMultiValue
235
+ }>
236
+ containerPosition$: Observable<ContainerPositionScaled[]>
237
+ layout$: Observable<Layout>
238
+ }) => {
239
+ const rootMousemove$ = d3EventObservable(rootSelection, 'mousemove').pipe(
240
+ debounceTime(2) // 避免過度頻繁觸發,實測時沒加電腦容易卡頓
241
+ )
242
+
243
+ const columnAmount$ = containerPosition$.pipe(
244
+ map(containerPosition => {
245
+ const maxColumnIndex = containerPosition.reduce((acc, current) => {
246
+ return current.columnIndex > acc ? current.columnIndex : acc
247
+ }, 0)
248
+ return maxColumnIndex + 1
249
+ }),
250
+ distinctUntilChanged(),
251
+ shareReplay(1)
252
+ )
253
+
254
+ const rowAmount$ = containerPosition$.pipe(
255
+ map(containerPosition => {
256
+ const maxRowIndex = containerPosition.reduce((acc, current) => {
257
+ return current.rowIndex > acc ? current.rowIndex : acc
258
+ }, 0)
259
+ return maxRowIndex + 1
260
+ }),
261
+ distinctUntilChanged(),
262
+ shareReplay(1)
263
+ )
264
+
265
+ // const xyScale$ = combineLatest({
266
+ // layout: layout$,
267
+ // filteredXYMinMaxData: filteredXYMinMaxData$,
268
+ // fullDataFormatter: fullDataFormatter$,
269
+ // columnAmount: columnAmount$,
270
+ // rowAmount: rowAmount$
271
+ // }).pipe(
272
+ // switchMap(async d => d),
273
+ // map(data => {
274
+ // const xScale = createAxisToValueScale({
275
+ // maxValue: data.filteredXYMinMaxData.maxXDatum.value[0],
276
+ // minValue: data.filteredXYMinMaxData.minXDatum.value[0],
277
+ // axisWidth: data.layout.width,
278
+ // scaleDomain: data.fullDataFormatter.xAxis.scaleDomain,
279
+ // scaleRange: data.fullDataFormatter.xAxis.scaleRange,
280
+ // })
281
+ // const yScale = createAxisToValueScale({
282
+ // maxValue: data.filteredXYMinMaxData.maxYDatum.value[1],
283
+ // minValue: data.filteredXYMinMaxData.minYDatum.value[1],
284
+ // axisWidth: data.layout.height,
285
+ // scaleDomain: data.fullDataFormatter.yAxis.scaleDomain,
286
+ // scaleRange: data.fullDataFormatter.yAxis.scaleRange,
287
+ // reverse: true
288
+ // })
289
+ // return { xScale, yScale }
290
+ // })
291
+ // )
292
+
293
+ const xyScale$: Observable<{
294
+ xScale: d3.ScaleLinear<number, number, never>;
295
+ yScale: d3.ScaleLinear<number, number, never>;
296
+ }> = new Observable(subscriber => {
297
+ combineLatest({
298
+ layout: layout$,
299
+ filteredXYMinMaxData: filteredXYMinMaxData$,
300
+ fullDataFormatter: fullDataFormatter$,
301
+ columnAmount: columnAmount$,
302
+ rowAmount: rowAmount$
303
+ }).pipe(
304
+ switchMap(async d => d),
305
+ ).subscribe(data => {
306
+ const xValueIndex = data.fullDataFormatter.xAxis.valueIndex
307
+ const yValueIndex = data.fullDataFormatter.yAxis.valueIndex
308
+ if (!data.filteredXYMinMaxData.minXDatum || !data.filteredXYMinMaxData.maxXDatum
309
+ || data.filteredXYMinMaxData.minXDatum.value[xValueIndex] == null || data.filteredXYMinMaxData.maxXDatum.value[xValueIndex] == null
310
+ || !data.filteredXYMinMaxData.minYDatum || !data.filteredXYMinMaxData.maxYDatum
311
+ || data.filteredXYMinMaxData.minYDatum.value[yValueIndex] == null || data.filteredXYMinMaxData.maxYDatum.value[yValueIndex] == null
312
+ ) {
313
+ return
314
+ }
315
+ const xScale = createAxisToValueScale({
316
+ maxValue: data.filteredXYMinMaxData.maxXDatum.value[xValueIndex],
317
+ minValue: data.filteredXYMinMaxData.minXDatum.value[xValueIndex],
318
+ axisWidth: data.layout.width,
319
+ scaleDomain: data.fullDataFormatter.xAxis.scaleDomain,
320
+ scaleRange: data.fullDataFormatter.xAxis.scaleRange,
321
+ })
322
+ const yScale = createAxisToValueScale({
323
+ maxValue: data.filteredXYMinMaxData.maxYDatum.value[yValueIndex],
324
+ minValue: data.filteredXYMinMaxData.minYDatum.value[yValueIndex],
325
+ axisWidth: data.layout.height,
326
+ scaleDomain: data.fullDataFormatter.yAxis.scaleDomain,
327
+ scaleRange: data.fullDataFormatter.yAxis.scaleRange,
328
+ reverse: true
329
+ })
330
+ subscriber.next({ xScale, yScale })
331
+ })
332
+ })
333
+
334
+ const axisValue$ = combineLatest({
335
+ fullDataFormatter: fullDataFormatter$,
336
+ rootMousemove: rootMousemove$,
337
+ columnAmount: columnAmount$,
338
+ rowAmount: rowAmount$,
339
+ layout: layout$,
340
+ containerPosition: containerPosition$
341
+ }).pipe(
342
+ switchMap(async d => d),
343
+ map(data => {
344
+ // 由於event座標是基於底層的,但是container會有多欄,所以要重新計算
345
+ return {
346
+ x: ((data.rootMousemove.offsetX - data.layout.left) / data.containerPosition[0].scale[0])
347
+ % (data.layout.rootWidth / data.columnAmount / data.containerPosition[0].scale[0]),
348
+ y: ((data.rootMousemove.offsetY - data.layout.top) / data.containerPosition[0].scale[1])
349
+ % (data.layout.rootHeight / data.rowAmount / data.containerPosition[0].scale[1])
350
+ }
351
+ })
352
+ )
353
+
354
+ return combineLatest({
355
+ xyScale: xyScale$,
356
+ axisValue: axisValue$
357
+ }).pipe(
358
+ switchMap(async d => d),
359
+ map(data => {
360
+ return {
361
+ x: data.axisValue.x,
362
+ y: data.axisValue.y,
363
+ xValue: data.xyScale.xScale(data.axisValue.x),
364
+ yValue: data.xyScale.yScale(data.axisValue.y)
365
+ }
366
+ })
367
+ )
368
+ }
369
+
370
+ // 排名數量
371
+ export const computedRankingAmountObservable = ({ containerSize$, visibleComputedData$, textSizePx$, rankingAmount$ }: {
372
+ containerSize$: Observable<ContainerSize>
373
+ visibleComputedData$: Observable<ComputedDatumMultiValue[][]>
374
+ textSizePx$: Observable<number>
375
+ rankingAmount$: Observable<'auto' | number>
376
+ }) => {
377
+ const minLineHeightObservable = ({ textSizePx$ }: {
378
+ textSizePx$: Observable<number>
379
+ }) => {
380
+ return textSizePx$.pipe(
381
+ map(textSizePx => textSizePx * 2), // 2倍行高
382
+ shareReplay(1)
383
+ )
384
+ }
385
+
386
+ const containerHeightObservable = ({ minLineHeight$, containerSize$ }: {
387
+ minLineHeight$: Observable<number>
388
+ containerSize$: Observable<ContainerSize>
389
+ }) => {
390
+ return combineLatest({
391
+ minLineHeight: minLineHeight$,
392
+ containerSize: containerSize$
393
+ }).pipe(
394
+ switchMap(async (d) => d),
395
+ map(data => {
396
+ // 避免過小造成計算 scale 錯誤
397
+ return data.containerSize.height > data.minLineHeight
398
+ ? data.containerSize.height
399
+ : data.minLineHeight
400
+ }),
401
+ distinctUntilChanged(),
402
+ shareReplay(1)
403
+ )
404
+ }
405
+
406
+ const rankingAmountLimitObservable = ({ minLineHeight$, containerHeight$ }: {
407
+ containerHeight$: Observable<number>
408
+ minLineHeight$: Observable<number>
409
+ }) => {
410
+
411
+ return combineLatest({
412
+ minLineHeight: minLineHeight$,
413
+ containerHeight: containerHeight$
414
+ }).pipe(
415
+ switchMap(async (d) => d),
416
+ map(data => {
417
+ const labelAmountLimit = Math.floor(data.containerHeight / data.minLineHeight)
418
+ return labelAmountLimit
419
+ }),
420
+ distinctUntilChanged(),
421
+ shareReplay(1)
422
+ )
423
+ }
424
+
425
+ const minLineHeight$ = minLineHeightObservable({ textSizePx$ })
426
+
427
+ const containerHeight$ = containerHeightObservable({
428
+ minLineHeight$,
429
+ containerSize$
430
+ })
431
+
432
+ const rankingAmountLimit$ = rankingAmountLimitObservable({
433
+ containerHeight$,
434
+ minLineHeight$
435
+ })
436
+
437
+ // 計算要排名的數量
438
+ return rankingAmount$.pipe(
439
+ switchMap(rankingAmount => {
440
+ return iif(
441
+ () => rankingAmount === 'auto',
442
+ // 'auto': 不超過限制
443
+ combineLatest({
444
+ visibleComputedData: visibleComputedData$,
445
+ rankingAmountLimit: rankingAmountLimit$,
446
+ }).pipe(
447
+ switchMap(async d => d),
448
+ map(data => {
449
+ const rankingAmountArr = data.visibleComputedData.map(categoryData => {
450
+ return Math.min(data.rankingAmountLimit, categoryData.length)
451
+ })
452
+ return Math.max(...rankingAmountArr) // 取所有 container 計算出來的最大值
453
+ })
454
+ ),
455
+ // number: 指定數量
456
+ rankingAmount$ as Observable<number>,
457
+ )
458
+ })
459
+ )
460
+ }
461
+
462
+ export const rankingItemHeightObservable = ({ containerSize$, textSizePx$, computedRankingAmount$ }: {
463
+ containerSize$: Observable<ContainerSize>
464
+ // visibleComputedRankingData$: Observable<ComputedDatumMultiValue[][]>
465
+ textSizePx$: Observable<number>
466
+ // rankingAmount$: Observable<'auto' | number>
467
+ computedRankingAmount$: Observable<number>
468
+ }) => {
469
+ const minLineHeightObservable = ({ textSizePx$ }: {
470
+ textSizePx$: Observable<number>
471
+ }) => {
472
+ return textSizePx$.pipe(
473
+ map(textSizePx => textSizePx * 2), // 2倍行高
474
+ shareReplay(1)
475
+ )
476
+ }
477
+
478
+ const containerHeightObservable = ({ minLineHeight$, containerSize$ }: {
479
+ minLineHeight$: Observable<number>
480
+ containerSize$: Observable<ContainerSize>
481
+ }) => {
482
+ return combineLatest({
483
+ minLineHeight: minLineHeight$,
484
+ containerSize: containerSize$
485
+ }).pipe(
486
+ switchMap(async (d) => d),
487
+ map(data => {
488
+ // 避免過小造成計算 scale 錯誤
489
+ return data.containerSize.height > data.minLineHeight
490
+ ? data.containerSize.height
491
+ : data.minLineHeight
492
+ }),
493
+ distinctUntilChanged(),
494
+ shareReplay(1)
495
+ )
496
+ }
497
+
498
+ const minLineHeight$ = minLineHeightObservable({ textSizePx$ })
499
+
500
+ const containerHeight$ = containerHeightObservable({
501
+ minLineHeight$,
502
+ containerSize$
503
+ })
504
+
505
+ return combineLatest({
506
+ containerHeight: containerHeight$,
507
+ computedRankingAmount: computedRankingAmount$
508
+ }).pipe(
509
+ switchMap(async (d) => d),
510
+ map(data => {
511
+ // // 依每個 category 計算 scale
512
+ // return data.visibleComputedRankingData.map((categoryData, i) => {
513
+ // const rankingAmount = data.computedRankingAmountList[i]
514
+ // const rankingItemHeight = data.containerHeight / rankingAmount
515
+ // return rankingItemHeight
516
+ // })
517
+ const rankingItemHeight = data.containerHeight / data.computedRankingAmount
518
+ return rankingItemHeight
519
+ })
520
+ )
521
+ }
522
+
523
+ export const rankingScaleListObservable = ({ visibleComputedRankingData$, rankingItemHeight$ }: {
524
+ visibleComputedRankingData$: Observable<ComputedDatumMultiValue[][]>
525
+ rankingItemHeight$: Observable<number>
526
+ }) => {
527
+
528
+ return combineLatest({
529
+ visibleComputedRankingData: visibleComputedRankingData$,
530
+ rankingItemHeight: rankingItemHeight$,
531
+ }).pipe(
532
+ switchMap(async (d) => d),
533
+ map(data => {
534
+ // 依每個 category 計算 scale
535
+ return data.visibleComputedRankingData.map((categoryData, i) => {
536
+ const allLabelAmount = categoryData.length
537
+ // const rankingItemHeight = data.rankingItemHeightList[i]
538
+ const totalHeight = data.rankingItemHeight * allLabelAmount // 有可能超出圖軸高度
539
+
540
+ return createLabelToAxisScale({
541
+ axisLabels: categoryData.map(d => d.label),
542
+ axisWidth: totalHeight,
543
+ padding: 0.5
544
+ })
545
+ })
546
+ })
547
+ )
548
+ }
549
+
550
+
551
+ // // Ranking資料 - 有 XY 資料 @Q@ 若沒用到要棄用
552
+ // export const computedRankingWithXYDataObservable = ({ visibleComputedRankingData$, computedRankingAmountList$, xyValueIndex$, layout$ }: {
553
+ // visibleComputedRankingData$: Observable<ComputedDatumMultiValue[][]>
554
+ // computedRankingAmountList$: Observable<number[]>
555
+ // xyValueIndex$: Observable<[number, number]>
556
+ // layout$: Observable<Layout>
557
+ // }): Observable<ComputedXYDataMultiValue> => {
558
+
559
+ // // // 未篩選範圍前的 scale
560
+ // // function createOriginXScale (xMinMax: { minX: number, maxX: number }, layout: Layout) {
561
+ // // let maxValue = xMinMax.maxX
562
+ // // let minValue = xMinMax.minX
563
+ // // if (minValue === maxValue && maxValue === 0) {
564
+ // // // 避免最大及最小值相同造成無法計算scale
565
+ // // maxValue = 1
566
+ // // }
567
+ // // const valueScale: d3.ScaleLinear<number, number> = createValueToAxisScale({
568
+ // // maxValue,
569
+ // // minValue,
570
+ // // axisWidth: layout.width,
571
+ // // scaleDomain: ['auto', 'auto'], // 不使用dataFormatter設定 --> 以0為基準到最大或最小值為範圍( * 如果是使用[minValue, maxValue]的話,在兩者很接近的情況下有可能造成scale倍率過高而svg變型時失真的情況)
572
+ // // scaleRange: [0, 1] // 不使用dataFormatter設定
573
+ // // })
574
+
575
+ // // return valueScale
576
+ // // }
577
+
578
+ // // 未篩選範圍及visible前的 scale
579
+ // function createOriginYScale (yMinMax: { minY: number, maxY: number }, layout: Layout) {
580
+ // let maxValue = yMinMax.maxY
581
+ // let minValue = yMinMax.minY
582
+ // if (minValue === maxValue && maxValue === 0) {
583
+ // // 避免最大及最小值相同造成無法計算scale
584
+ // maxValue = 1
585
+ // }
586
+ // const valueScale: d3.ScaleLinear<number, number> = createValueToAxisScale({
587
+ // maxValue,
588
+ // minValue,
589
+ // axisWidth: layout.height,
590
+ // scaleDomain: ['auto', 'auto'], // 不使用dataFormatter設定 --> 以0為基準到最大或最小值為範圍( * 如果是使用[minValue, maxValue]的話,在兩者很接近的情況下有可能造成scale倍率過高而svg變型時失真的情況)
591
+ // scaleRange: [0, 1], // 不使用dataFormatter設定
592
+ // // reverse: true
593
+ // })
594
+
595
+ // return valueScale
596
+ // }
597
+
598
+ // return combineLatest({
599
+ // visibleComputedRankingData: visibleComputedRankingData$,
600
+ // computedRankingAmountList: computedRankingAmountList$,
601
+ // xyValueIndex: xyValueIndex$,
602
+ // layout: layout$
603
+ // }).pipe(
604
+ // switchMap(async d => d),
605
+ // map(data => {
606
+
607
+ // // const maxX = data.visibleComputedRankingData
608
+ // // .flat()
609
+ // // .reduce((acc, current) => {
610
+ // // const maxXIndex = current.value.length - 1
611
+ // // return maxXIndex > acc ? maxXIndex : acc
612
+ // // }, 0)
613
+ // // const xMinMax = {
614
+ // // minX: 0,
615
+ // // maxX
616
+ // // }
617
+ // // const xScale = createOriginXScale(xMinMax, data.layout)
618
+ // // console.log('data.visibleComputedRankingData', data.visibleComputedRankingData)
619
+ // return data.visibleComputedRankingData
620
+ // .map((categoryData, categoryIndex) => {
621
+ // const yMinMax = {
622
+ // minY: 0,
623
+ // maxY: data.computedRankingAmountList[categoryIndex]
624
+ // }
625
+ // const yScale = createOriginYScale(yMinMax, data.layout)
626
+
627
+ // return categoryData.map((datum, datumIndex) => {
628
+ // return {
629
+ // ...datum,
630
+ // // axisX: xScale(datum.value[data.xyValueIndex[0]] ?? 0),
631
+ // axisX: 0,
632
+ // // axisY: yScale(datum.value[data.xyValueIndex[1]] ?? 0), // y軸的繪圖座標是從上到下,所以反轉
633
+ // axisY: yScale(datumIndex),
634
+ // }
635
+ // })
636
+ // })
637
+ // })
638
+ // )
639
+ // }
640
+
641
+ // // Ranking資料 - 有 XY 資料 @Q@ 若沒用到要棄用
642
+ // export const computedRankingWithXYDataObservable = ({ visibleComputedRankingData$, rankingScaleList$ }: {
643
+ // visibleComputedRankingData$: Observable<ComputedDatumMultiValue[][]>
644
+ // rankingScaleList$: Observable<d3.ScalePoint<string>[]>
645
+ // }): Observable<ComputedXYDataMultiValue> => {
646
+
647
+ // return combineLatest({
648
+ // visibleComputedRankingData: visibleComputedRankingData$,
649
+ // rankingScaleList: rankingScaleList$
650
+ // }).pipe(
651
+ // switchMap(async d => d),
652
+ // map(data => {
653
+ // return data.visibleComputedRankingData
654
+ // .map((categoryData, categoryIndex) => {
655
+ // const yScale = data.rankingScaleList[categoryIndex]
656
+
657
+ // return categoryData.map((datum, datumIndex) => {
658
+ // return {
659
+ // ...datum,
660
+ // axisX: 0,
661
+ // axisY: yScale(datum.label),
662
+ // }
663
+ // })
664
+ // })
665
+ // })
666
+ // )
667
+ // }