@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,555 +1,613 @@
1
- import * as d3 from 'd3'
2
- import {
3
- Observable,
4
- Subject,
5
- of,
6
- takeUntil,
7
- filter,
8
- map,
9
- switchMap,
10
- combineLatest,
11
- merge,
12
- shareReplay,
13
- distinctUntilChanged
14
- } from 'rxjs'
15
- import type {
16
- ChartParams,
17
- HighlightTarget,
18
- DataFormatterGrid,
19
- ComputedDataGrid,
20
- ComputedDatumGrid,
21
- TransformData,
22
- ContainerPositionScaled,
23
- Layout } from '../../lib/core-types'
24
- import { createAxisToLabelIndexScale } from '../../lib/core'
25
- import { getClassName, getUniID } from '../utils/orbchartsUtils'
26
- import { d3EventObservable } from '../utils/observables'
27
-
28
- // 建立 grid 主要的 selection
29
- export const gridSelectionsObservable = ({ selection, pluginName, clipPathID, seriesLabels$, gridContainerPosition$, gridAxesTransform$, gridGraphicTransform$ }: {
30
- selection: d3.Selection<any, unknown, any, unknown>
31
- pluginName: string
32
- clipPathID: string
33
- // computedData$: Observable<ComputedDataGrid>
34
- seriesLabels$: Observable<string[]>
35
- gridContainerPosition$: Observable<ContainerPositionScaled[]>
36
- gridAxesTransform$: Observable<TransformData>
37
- gridGraphicTransform$: Observable<TransformData>
38
- }) => {
39
- const seriesClassName = getClassName(pluginName, 'series')
40
- const axesClassName = getClassName(pluginName, 'axes')
41
- const graphicClassName = getClassName(pluginName, 'graphic')
42
-
43
- // <g> series selection(container排放位置)
44
- // <g> axes selection(旋轉圖軸方向)
45
- // <defs> clipPath selection
46
- // <g> graphic selection(圖形 scale 範圍的變形)
47
- const seriesSelection$ = seriesLabels$.pipe(
48
- map((seriesLabels, i) => {
49
- return selection
50
- .selectAll<SVGGElement, string>(`g.${seriesClassName}`)
51
- .data(seriesLabels, d => d)
52
- .join(
53
- enter => {
54
- return enter
55
- .append('g')
56
- .classed(seriesClassName, true)
57
- .each((d, i, g) => {
58
- const axesSelection = d3.select(g[i])
59
- .selectAll<SVGGElement, ComputedDatumGrid[]>(`g.${axesClassName}`)
60
- .data([i])
61
- .join(
62
- enter => {
63
- return enter
64
- .append('g')
65
- .classed(axesClassName, true)
66
- .attr('clip-path', `url(#${clipPathID})`)
67
- .each((d, i, g) => {
68
- const defsSelection = d3.select(g[i])
69
- .selectAll<SVGDefsElement, any>('defs')
70
- .data([i])
71
- .join('defs')
72
-
73
- const graphicGSelection = d3.select(g[i])
74
- .selectAll<SVGGElement, any>('g')
75
- .data([i])
76
- .join('g')
77
- .classed(graphicClassName, true)
78
- })
79
- },
80
- update => update,
81
- exit => exit.remove()
82
- )
83
- })
84
- },
85
- update => update,
86
- exit => exit.remove()
87
- )
88
- }),
89
- shareReplay(1)
90
- )
91
-
92
- // <g> series selection
93
- combineLatest({
94
- seriesSelection: seriesSelection$,
95
- gridContainerPosition: gridContainerPosition$
96
- }).pipe(
97
- switchMap(async d => d)
98
- ).subscribe(data => {
99
- data.seriesSelection
100
- .transition()
101
- .attr('transform', (d, i) => {
102
- const gridContainerPosition = data.gridContainerPosition[i] ?? data.gridContainerPosition[0]
103
- const translate = gridContainerPosition.translate
104
- const scale = gridContainerPosition.scale
105
- return `translate(${translate[0]}, ${translate[1]}) scale(${scale[0]}, ${scale[1]})`
106
- })
107
- })
108
-
109
- // <g> axes selection
110
- const axesSelection$ = combineLatest({
111
- seriesSelection: seriesSelection$,
112
- gridAxesTransform: gridAxesTransform$
113
- }).pipe(
114
- switchMap(async d => d),
115
- map(data => {
116
- return data.seriesSelection
117
- .select<SVGGElement>(`g.${axesClassName}`)
118
- .style('transform', data.gridAxesTransform.value)
119
- }),
120
- shareReplay(1)
121
- )
122
-
123
- // <defs> clipPath selection
124
- const defsSelection$ = axesSelection$.pipe(
125
- map(axesSelection => {
126
- return axesSelection.select<SVGDefsElement>('defs')
127
- }),
128
- shareReplay(1)
129
- )
130
-
131
- // <g> graphic selection
132
- const graphicGSelection$ = combineLatest({
133
- axesSelection: axesSelection$,
134
- gridGraphicTransform: gridGraphicTransform$
135
- }).pipe(
136
- switchMap(async d => d),
137
- map(data => {
138
- const graphicGSelection = data.axesSelection
139
- .select<SVGGElement>(`g.${graphicClassName}`)
140
- graphicGSelection
141
- .transition()
142
- .duration(50)
143
- .style('transform', data.gridGraphicTransform.value)
144
- return graphicGSelection
145
- }),
146
- shareReplay(1)
147
- )
148
-
149
- return {
150
- seriesSelection$,
151
- axesSelection$,
152
- defsSelection$,
153
- graphicGSelection$
154
- }
155
- }
156
-
157
- // 由事件取得group data的function
158
- export const gridGroupPositionFnObservable = ({ fullDataFormatter$, gridAxesSize$, computedData$, fullChartParams$, gridContainerPosition$, layout$ }: {
159
- fullDataFormatter$: Observable<DataFormatterGrid>
160
- gridAxesSize$: Observable<{
161
- width: number;
162
- height: number;
163
- }>
164
- computedData$: Observable<ComputedDataGrid>
165
- // GroupDataMap$: Observable<Map<string, ComputedDatumGrid[]>>
166
- fullChartParams$: Observable<ChartParams>
167
- gridContainerPosition$: Observable<ContainerPositionScaled[]>
168
- layout$: Observable<Layout>
169
- }): Observable<(event: any) => { groupIndex: number; groupLabel: string }> => {
170
- const destroy$ = new Subject()
171
-
172
- // 顯示範圍內的group labels
173
- // const scaleRangeGroupLabels$: Observable<string[]> = new Observable(subscriber => {
174
- // combineLatest({
175
- // dataFormatter: fullDataFormatter$,
176
- // computedData: computedData$
177
- // }).pipe(
178
- // takeUntil(destroy$),
179
- // switchMap(async (d) => d),
180
- // ).subscribe(data => {
181
- // const groupMin = 0
182
- // const groupMax = data.computedData[0] ? data.computedData[0].length - 1 : 0
183
- // const groupScaleDomainMin = data.dataFormatter.groupAxis.scaleDomain[0] === 'auto'
184
- // ? groupMin - data.dataFormatter.groupAxis.scalePadding
185
- // : data.dataFormatter.groupAxis.scaleDomain[0] as number - data.dataFormatter.groupAxis.scalePadding
186
- // const groupScaleDomainMax = data.dataFormatter.groupAxis.scaleDomain[1] === 'auto'
187
- // ? groupMax + data.dataFormatter.groupAxis.scalePadding
188
- // : data.dataFormatter.groupAxis.scaleDomain[1] as number + data.dataFormatter.groupAxis.scalePadding
189
-
190
- // // const groupingAmount = data.computedData[0]
191
- // // ? data.computedData[0].length
192
- // // : 0
193
-
194
- // let _labels = data.dataFormatter.seriesDirection === 'row'
195
- // ? (data.computedData[0] ?? []).map(d => d.groupLabel)
196
- // : data.computedData.map(d => d[0].groupLabel)
197
-
198
- // const _axisLabels =
199
- // // new Array(groupingAmount).fill(0)
200
- // // .map((d, i) => {
201
- // // return _labels[i] != null
202
- // // ? _labels[i]
203
- // // : String(i) // 沒有label則用序列號填充
204
- // // })
205
- // _labels
206
- // .filter((d, i) => {
207
- // return i >= groupScaleDomainMin && i <= groupScaleDomainMax
208
- // })
209
- // subscriber.next(_axisLabels)
210
- // })
211
- // })
212
- const groupScaleDomain$ = combineLatest({
213
- fullDataFormatter: fullDataFormatter$,
214
- gridAxesSize: gridAxesSize$,
215
- computedData: computedData$
216
- }).pipe(
217
- switchMap(async (d) => d),
218
- map(data => {
219
- const groupMin = 0
220
- const groupMax = data.computedData[0] ? data.computedData[0].length - 1 : 0
221
- // const groupScaleDomainMin = data.fullDataFormatter.groupAxis.scaleDomain[0] === 'auto'
222
- // ? groupMin - data.fullDataFormatter.groupAxis.scalePadding
223
- // : data.fullDataFormatter.groupAxis.scaleDomain[0] as number - data.fullDataFormatter.groupAxis.scalePadding
224
- const groupScaleDomainMin = data.fullDataFormatter.groupAxis.scaleDomain[0] - data.fullDataFormatter.groupAxis.scalePadding
225
- const groupScaleDomainMax = data.fullDataFormatter.groupAxis.scaleDomain[1] === 'max'
226
- ? groupMax + data.fullDataFormatter.groupAxis.scalePadding
227
- : data.fullDataFormatter.groupAxis.scaleDomain[1] as number + data.fullDataFormatter.groupAxis.scalePadding
228
-
229
- return [groupScaleDomainMin, groupScaleDomainMax]
230
- }),
231
- shareReplay(1)
232
- )
233
-
234
- const groupLabels$ = combineLatest({
235
- fullDataFormatter: fullDataFormatter$,
236
- computedData: computedData$
237
- }).pipe(
238
- switchMap(async d => d),
239
- map(data => {
240
- return data.fullDataFormatter.seriesDirection === 'row'
241
- ? (data.computedData[0] ?? []).map(d => d.groupLabel)
242
- : data.computedData.map(d => d[0].groupLabel)
243
- })
244
- )
245
-
246
- // 顯示範圍內的group labels
247
- const scaleRangeGroupLabels$ = combineLatest({
248
- groupScaleDomain: groupScaleDomain$,
249
- groupLabels: groupLabels$
250
- }).pipe(
251
- switchMap(async d => d),
252
- map(data => {
253
- return data.groupLabels
254
- .filter((d, i) => {
255
- return i >= data.groupScaleDomain[0] && i <= data.groupScaleDomain[1]
256
- })
257
- })
258
- )
259
-
260
- const columnAmount$ = gridContainerPosition$.pipe(
261
- map(gridContainerPosition => {
262
- const maxColumnIndex = gridContainerPosition.reduce((acc, current) => {
263
- return current.columnIndex > acc ? current.columnIndex : acc
264
- }, 0)
265
- return maxColumnIndex + 1
266
- }),
267
- distinctUntilChanged()
268
- )
269
-
270
- const rowAmount$ = gridContainerPosition$.pipe(
271
- map(gridContainerPosition => {
272
- const maxRowIndex = gridContainerPosition.reduce((acc, current) => {
273
- return current.rowIndex > acc ? current.rowIndex : acc
274
- }, 0)
275
- return maxRowIndex + 1
276
- }),
277
- distinctUntilChanged()
278
- )
279
-
280
- return new Observable<(event: any) => { groupIndex: number; groupLabel: string }>(subscriber => {
281
- combineLatest({
282
- dataFormatter: fullDataFormatter$,
283
- axisSize: gridAxesSize$,
284
- fullChartParams: fullChartParams$,
285
- scaleRangeGroupLabels: scaleRangeGroupLabels$,
286
- groupLabels: groupLabels$,
287
- groupScaleDomain: groupScaleDomain$,
288
- columnAmount: columnAmount$,
289
- rowAmount: rowAmount$,
290
- layout: layout$
291
- }).pipe(
292
- takeUntil(destroy$),
293
- switchMap(async (d) => d),
294
- ).subscribe(data => {
295
-
296
- const reverse = data.dataFormatter.valueAxis.position === 'right'
297
- || data.dataFormatter.valueAxis.position === 'bottom'
298
- ? true : false
299
-
300
- // 比例尺座標對應非連續資料索引
301
- const xIndexScale = createAxisToLabelIndexScale({
302
- axisLabels: data.scaleRangeGroupLabels,
303
- axisWidth: data.axisSize.width,
304
- padding: data.dataFormatter.groupAxis.scalePadding,
305
- reverse
306
- })
307
-
308
- // 依比例尺位置計算座標
309
- const axisValuePredicate = (event: any) => {
310
- return data.dataFormatter.groupAxis.position === 'bottom'
311
- || data.dataFormatter.groupAxis.position === 'top'
312
- ? event.offsetX - data.fullChartParams.padding.left
313
- : event.offsetY - data.fullChartParams.padding.top
314
- }
315
-
316
- // 比例尺座標取得groupData的function
317
- const createEventGroupData: (event: MouseEvent) => { groupIndex: number; groupLabel: string } = (event: any) => {
318
- // 由於event座標是基於底層的,但是container會有多欄,所以要重新計算
319
- const eventData = {
320
- offsetX: event.offsetX * data.columnAmount % data.layout.rootWidth,
321
- offsetY: event.offsetY * data.rowAmount % data.layout.rootHeight
322
- }
323
- // console.log('data.columnAmount', data.columnAmount, 'data.rowAmount', data.rowAmount, 'data.layout.rootWidth', data.layout.rootWidth, 'data.layout.rootHeight', data.layout.rootHeight)
324
- const axisValue = axisValuePredicate(eventData)
325
- const xIndex = xIndexScale(axisValue)
326
- const currentxIndexStart = Math.ceil(data.groupScaleDomain[0]) // 因為有padding所以會有小數點,所以要無條件進位
327
- const groupIndex = xIndex + currentxIndexStart
328
-
329
- return {
330
- groupIndex,
331
- groupLabel: data.groupLabels[groupIndex] ?? ''
332
- }
333
- }
334
-
335
- subscriber.next(createEventGroupData)
336
-
337
- return function unsubscribe () {
338
- destroy$.next(undefined)
339
- }
340
- })
341
- })
342
- }
343
-
344
- export const gridGroupPositionObservable = ({ rootSelection, fullDataFormatter$, gridAxesSize$, computedData$, fullChartParams$, gridContainerPosition$, layout$ }: {
345
- rootSelection: d3.Selection<any, unknown, any, unknown>
346
- fullDataFormatter$: Observable<DataFormatterGrid>
347
- gridAxesSize$: Observable<{
348
- width: number;
349
- height: number;
350
- }>
351
- computedData$: Observable<ComputedDataGrid>
352
- fullChartParams$: Observable<ChartParams>
353
- gridContainerPosition$: Observable<ContainerPositionScaled[]>
354
- layout$: Observable<Layout>
355
- }) => {
356
- const rootMousemove$ = d3EventObservable(rootSelection, 'mousemove')
357
-
358
- const groupScaleDomain$ = combineLatest({
359
- fullDataFormatter: fullDataFormatter$,
360
- gridAxesSize: gridAxesSize$,
361
- computedData: computedData$
362
- }).pipe(
363
- switchMap(async (d) => d),
364
- map(data => {
365
- const groupMin = 0
366
- const groupMax = data.computedData[0] ? data.computedData[0].length - 1 : 0
367
- // const groupScaleDomainMin = data.fullDataFormatter.groupAxis.scaleDomain[0] === 'auto'
368
- // ? groupMin - data.fullDataFormatter.groupAxis.scalePadding
369
- // : data.fullDataFormatter.groupAxis.scaleDomain[0] as number - data.fullDataFormatter.groupAxis.scalePadding
370
- const groupScaleDomainMin = data.fullDataFormatter.groupAxis.scaleDomain[0] - data.fullDataFormatter.groupAxis.scalePadding
371
- const groupScaleDomainMax = data.fullDataFormatter.groupAxis.scaleDomain[1] === 'max'
372
- ? groupMax + data.fullDataFormatter.groupAxis.scalePadding
373
- : data.fullDataFormatter.groupAxis.scaleDomain[1] as number + data.fullDataFormatter.groupAxis.scalePadding
374
-
375
- return [groupScaleDomainMin, groupScaleDomainMax]
376
- }),
377
- shareReplay(1)
378
- )
379
-
380
- const groupLabels$ = combineLatest({
381
- fullDataFormatter: fullDataFormatter$,
382
- computedData: computedData$
383
- }).pipe(
384
- switchMap(async d => d),
385
- map(data => {
386
- return data.fullDataFormatter.seriesDirection === 'row'
387
- ? (data.computedData[0] ?? []).map(d => d.groupLabel)
388
- : data.computedData.map(d => d[0].groupLabel)
389
- })
390
- )
391
-
392
- const scaleRangeGroupLabels$ = combineLatest({
393
- groupScaleDomain: groupScaleDomain$,
394
- groupLabels: groupLabels$
395
- }).pipe(
396
- switchMap(async d => d),
397
- map(data => {
398
- return data.groupLabels
399
- .filter((d, i) => {
400
- return i >= data.groupScaleDomain[0] && i <= data.groupScaleDomain[1]
401
- })
402
- })
403
- )
404
-
405
- const reverse$ = fullDataFormatter$.pipe(
406
- map(d => {
407
- return d.valueAxis.position === 'right' || d.valueAxis.position === 'bottom'
408
- ? true
409
- : false
410
- })
411
- )
412
-
413
- // 比例尺座標對應非連續資料索引
414
- const xIndexScale$ = combineLatest({
415
- reverse: reverse$,
416
- gridAxesSize: gridAxesSize$,
417
- scaleRangeGroupLabels: scaleRangeGroupLabels$,
418
- fullDataFormatter: fullDataFormatter$
419
- }).pipe(
420
- switchMap(async d => d),
421
- map(data => {
422
- return createAxisToLabelIndexScale({
423
- axisLabels: data.scaleRangeGroupLabels,
424
- axisWidth: data.gridAxesSize.width,
425
- padding: data.fullDataFormatter.groupAxis.scalePadding,
426
- reverse: data.reverse
427
- })
428
- })
429
- )
430
-
431
- const columnAmount$ = gridContainerPosition$.pipe(
432
- map(gridContainerPosition => {
433
- const maxColumnIndex = gridContainerPosition.reduce((acc, current) => {
434
- return current.columnIndex > acc ? current.columnIndex : acc
435
- }, 0)
436
- return maxColumnIndex + 1
437
- }),
438
- distinctUntilChanged()
439
- )
440
-
441
- const rowAmount$ = gridContainerPosition$.pipe(
442
- map(gridContainerPosition => {
443
- const maxRowIndex = gridContainerPosition.reduce((acc, current) => {
444
- return current.rowIndex > acc ? current.rowIndex : acc
445
- }, 0)
446
- return maxRowIndex + 1
447
- }),
448
- distinctUntilChanged()
449
- )
450
-
451
- const axisValue$ = combineLatest({
452
- fullDataFormatter: fullDataFormatter$,
453
- fullChartParams: fullChartParams$,
454
- rootMousemove: rootMousemove$,
455
- columnAmount: columnAmount$,
456
- rowAmount: rowAmount$,
457
- layout: layout$
458
- }).pipe(
459
- switchMap(async d => d),
460
- map(data => {
461
- // 由於event座標是基於底層的,但是container會有多欄,所以要重新計算
462
- const eventData = {
463
- offsetX: data.rootMousemove.offsetX * data.columnAmount % data.layout.rootWidth,
464
- offsetY: data.rootMousemove.offsetY * data.rowAmount % data.layout.rootHeight
465
- }
466
- return data.fullDataFormatter.groupAxis.position === 'bottom'
467
- || data.fullDataFormatter.groupAxis.position === 'top'
468
- ? eventData.offsetX - data.fullChartParams.padding.left
469
- : eventData.offsetY - data.fullChartParams.padding.top
470
- })
471
- )
472
-
473
- const groupIndex$ = combineLatest({
474
- xIndexScale: xIndexScale$,
475
- axisValue: axisValue$,
476
- groupScaleDomain: groupScaleDomain$
477
- }).pipe(
478
- switchMap(async d => d),
479
- map(data => {
480
- const xIndex = data.xIndexScale(data.axisValue)
481
- const currentxIndexStart = Math.ceil(data.groupScaleDomain[0]) // 因為有padding所以會有小數點,所以要無條件進位
482
- return xIndex + currentxIndexStart
483
- })
484
- )
485
-
486
- const groupLabel$ = combineLatest({
487
- groupIndex: groupIndex$,
488
- groupLabels: groupLabels$
489
- }).pipe(
490
- switchMap(async d => d),
491
- map(data => {
492
- return data.groupLabels[data.groupIndex] ?? ''
493
- })
494
- )
495
-
496
- return combineLatest({
497
- groupIndex: groupIndex$,
498
- groupLabel: groupLabel$
499
- }).pipe(
500
- switchMap(async d => d),
501
- map(data => {
502
- return {
503
- groupIndex: data.groupIndex,
504
- groupLabel: data.groupLabel
505
- }
506
- })
507
- )
508
- }
509
-
510
- // const gridContainerEventData$ = ({ eventData$, gridContainerPosition$, layout$ }: {
511
- // eventData$: Observable<any>
512
- // gridContainerPosition$: Observable<ContainerPositionScaled[]>
513
- // layout$: Observable<Layout>
514
- // }): Observable<{
515
- // offsetX: number;
516
- // offsetY: number;
517
- // }> => {
518
- // const columnAmount$ = gridContainerPosition$.pipe(
519
- // map(gridContainerPosition => {
520
- // const maxColumnIndex = gridContainerPosition.reduce((acc, current) => {
521
- // return current.columnIndex > acc ? current.columnIndex : acc
522
- // }, 0)
523
- // return maxColumnIndex + 1
524
- // }),
525
- // distinctUntilChanged()
526
- // )
527
-
528
- // const rowAmount$ = gridContainerPosition$.pipe(
529
- // map(gridContainerPosition => {
530
- // const maxRowIndex = gridContainerPosition.reduce((acc, current) => {
531
- // return current.rowIndex > acc ? current.rowIndex : acc
532
- // }, 0)
533
- // return maxRowIndex + 1
534
- // }),
535
- // distinctUntilChanged()
536
- // )
537
-
538
- // return combineLatest({
539
- // eventData: eventData$,
540
- // gridContainerPosition: gridContainerPosition$,
541
- // layout: layout$,
542
- // columnAmount: columnAmount$,
543
- // rowAmount: rowAmount$
544
- // }).pipe(
545
- // switchMap(async d => d),
546
- // map(data => {
547
- // // 由於event座標是基於底層的,但是container會有多欄,所以要重新計算
548
- // const eventData = {
549
- // offsetX: data.eventData.offsetX * data.columnAmount % data.layout.rootWidth,
550
- // offsetY: data.eventData.offsetY * data.rowAmount % data.layout.rootHeight
551
- // }
552
- // return eventData
553
- // })
554
- // )
1
+ import * as d3 from 'd3'
2
+ import {
3
+ Observable,
4
+ Subject,
5
+ of,
6
+ takeUntil,
7
+ filter,
8
+ map,
9
+ switchMap,
10
+ combineLatest,
11
+ merge,
12
+ shareReplay,
13
+ distinctUntilChanged
14
+ } from 'rxjs'
15
+ import type {
16
+ ChartParams,
17
+ HighlightTarget,
18
+ DataFormatterGrid,
19
+ ComputedDataGrid,
20
+ ComputedDatumGrid,
21
+ TransformData,
22
+ ContainerPositionScaled,
23
+ Layout } from '../../lib/core-types'
24
+ import { createAxisToLabelIndexScale } from '../../lib/core'
25
+ import { getClassName, getUniID } from '../utils/orbchartsUtils'
26
+ import { d3EventObservable } from '../utils/observables'
27
+
28
+ // 建立 grid 主要的 selection
29
+ export const gridSelectionsObservable = ({ selection, pluginName, clipPathID, seriesLabels$, gridContainerPosition$, gridAxesTransform$, gridGraphicTransform$ }: {
30
+ selection: d3.Selection<any, unknown, any, unknown>
31
+ pluginName: string
32
+ clipPathID: string
33
+ // computedData$: Observable<ComputedDataGrid>
34
+ seriesLabels$: Observable<string[]>
35
+ gridContainerPosition$: Observable<ContainerPositionScaled[]>
36
+ gridAxesTransform$: Observable<TransformData>
37
+ gridGraphicTransform$: Observable<TransformData>
38
+ }) => {
39
+ const seriesClassName = getClassName(pluginName, 'series')
40
+ const axesClassName = getClassName(pluginName, 'axes')
41
+ const graphicClassName = getClassName(pluginName, 'graphic')
42
+
43
+ // <g> series selection(container排放位置)
44
+ // <g> axes selection(旋轉圖軸方向)
45
+ // <defs> clipPath selection
46
+ // <g> graphic selection(圖形 scale 範圍的變形)
47
+ const seriesSelection$ = seriesLabels$.pipe(
48
+ map((seriesLabels, i) => {
49
+ return selection
50
+ .selectAll<SVGGElement, string>(`g.${seriesClassName}`)
51
+ .data(seriesLabels, d => d)
52
+ .join(
53
+ enter => {
54
+ return enter
55
+ .append('g')
56
+ .classed(seriesClassName, true)
57
+ .each((d, i, g) => {
58
+ const axesSelection = d3.select(g[i])
59
+ .selectAll<SVGGElement, ComputedDatumGrid[]>(`g.${axesClassName}`)
60
+ .data([i])
61
+ .join(
62
+ enter => {
63
+ return enter
64
+ .append('g')
65
+ .classed(axesClassName, true)
66
+ .attr('clip-path', `url(#${clipPathID})`)
67
+ .each((d, i, g) => {
68
+ const defsSelection = d3.select(g[i])
69
+ .selectAll<SVGDefsElement, any>('defs')
70
+ .data([i])
71
+ .join('defs')
72
+
73
+ const graphicGSelection = d3.select(g[i])
74
+ .selectAll<SVGGElement, any>('g')
75
+ .data([i])
76
+ .join('g')
77
+ .classed(graphicClassName, true)
78
+ })
79
+ },
80
+ update => update,
81
+ exit => exit.remove()
82
+ )
83
+ })
84
+ },
85
+ update => update,
86
+ exit => exit.remove()
87
+ )
88
+ }),
89
+ shareReplay(1)
90
+ )
91
+
92
+ // <g> series selection
93
+ combineLatest({
94
+ seriesSelection: seriesSelection$,
95
+ gridContainerPosition: gridContainerPosition$
96
+ }).pipe(
97
+ switchMap(async d => d)
98
+ ).subscribe(data => {
99
+ data.seriesSelection
100
+ .transition()
101
+ .attr('transform', (d, i) => {
102
+ const gridContainerPosition = data.gridContainerPosition[i] ?? data.gridContainerPosition[0]
103
+ const translate = gridContainerPosition.translate
104
+ const scale = gridContainerPosition.scale
105
+ return `translate(${translate[0]}, ${translate[1]}) scale(${scale[0]}, ${scale[1]})`
106
+ })
107
+ })
108
+
109
+ // <g> axes selection
110
+ const axesSelection$ = combineLatest({
111
+ seriesSelection: seriesSelection$,
112
+ gridAxesTransform: gridAxesTransform$
113
+ }).pipe(
114
+ switchMap(async d => d),
115
+ map(data => {
116
+ return data.seriesSelection
117
+ .select<SVGGElement>(`g.${axesClassName}`)
118
+ .style('transform', data.gridAxesTransform.value)
119
+ }),
120
+ shareReplay(1)
121
+ )
122
+
123
+ // <defs> clipPath selection
124
+ const defsSelection$ = axesSelection$.pipe(
125
+ map(axesSelection => {
126
+ return axesSelection.select<SVGDefsElement>('defs')
127
+ }),
128
+ shareReplay(1)
129
+ )
130
+
131
+ // <g> graphic selection
132
+ const graphicGSelection$ = combineLatest({
133
+ axesSelection: axesSelection$,
134
+ gridGraphicTransform: gridGraphicTransform$
135
+ }).pipe(
136
+ switchMap(async d => d),
137
+ map(data => {
138
+ const graphicGSelection = data.axesSelection
139
+ .select<SVGGElement>(`g.${graphicClassName}`)
140
+ graphicGSelection
141
+ .transition()
142
+ .duration(50)
143
+ .style('transform', data.gridGraphicTransform.value)
144
+ return graphicGSelection
145
+ }),
146
+ shareReplay(1)
147
+ )
148
+
149
+ return {
150
+ seriesSelection$,
151
+ axesSelection$,
152
+ defsSelection$,
153
+ graphicGSelection$
154
+ }
155
+ }
156
+
157
+ // 建立 grid 主要的 selection - 只取的container
158
+ export const gridContainerSelectionsObservable = ({ selection, pluginName, computedData$, gridContainerPosition$, isSeriesSeprate$ }: {
159
+ selection: d3.Selection<any, unknown, any, unknown>
160
+ pluginName: string
161
+ computedData$: Observable<ComputedDataGrid>
162
+ gridContainerPosition$: Observable<ContainerPositionScaled[]>
163
+ isSeriesSeprate$: Observable<boolean>
164
+ }) => {
165
+ const containerClassName = getClassName(pluginName, 'container')
166
+
167
+ const containerSelection$ = combineLatest({
168
+ computedData: computedData$.pipe(
169
+ distinctUntilChanged((a, b) => {
170
+ // 只有當series的數量改變時,才重新計算
171
+ return a.length === b.length
172
+ }),
173
+ ),
174
+ isSeriesSeprate: isSeriesSeprate$
175
+ }).pipe(
176
+ switchMap(async (d) => d),
177
+ map(data => {
178
+ return data.isSeriesSeprate
179
+ // series分開的時候顯示各別axis
180
+ ? data.computedData
181
+ // series合併的時候只顯示第一個axis
182
+ : [data.computedData[0]]
183
+ }),
184
+ map((computedData, i) => {
185
+ return selection
186
+ .selectAll<SVGGElement, ComputedDatumGrid[]>(`g.${containerClassName}`)
187
+ .data(computedData, d => d[0] ? d[0].seriesIndex : i)
188
+ .join('g')
189
+ .classed(containerClassName, true)
190
+ }),
191
+ shareReplay(1)
192
+ )
193
+
194
+ combineLatest({
195
+ containerSelection: containerSelection$,
196
+ gridContainerPosition: gridContainerPosition$
197
+ }).pipe(
198
+ switchMap(async d => d)
199
+ ).subscribe(data => {
200
+ data.containerSelection
201
+ .attr('transform', (d, i) => {
202
+ const gridContainerPosition = data.gridContainerPosition[i] ?? data.gridContainerPosition[0]
203
+ const translate = gridContainerPosition.translate
204
+ const scale = gridContainerPosition.scale
205
+ return `translate(${translate[0]}, ${translate[1]}) scale(${scale[0]}, ${scale[1]})`
206
+ })
207
+ // .attr('opacity', 0)
208
+ // .transition()
209
+ // .attr('opacity', 1)
210
+ })
211
+
212
+ return containerSelection$
213
+ }
214
+
215
+ // 由事件取得group data的function
216
+ export const gridGroupPositionFnObservable = ({ fullDataFormatter$, gridAxesSize$, computedData$, fullChartParams$, gridContainerPosition$, layout$ }: {
217
+ fullDataFormatter$: Observable<DataFormatterGrid>
218
+ gridAxesSize$: Observable<{
219
+ width: number;
220
+ height: number;
221
+ }>
222
+ computedData$: Observable<ComputedDataGrid>
223
+ // GroupDataMap$: Observable<Map<string, ComputedDatumGrid[]>>
224
+ fullChartParams$: Observable<ChartParams>
225
+ gridContainerPosition$: Observable<ContainerPositionScaled[]>
226
+ layout$: Observable<Layout>
227
+ }): Observable<(event: any) => { groupIndex: number; groupLabel: string }> => {
228
+ const destroy$ = new Subject()
229
+
230
+ // 顯示範圍內的group labels
231
+ // const scaleRangeGroupLabels$: Observable<string[]> = new Observable(subscriber => {
232
+ // combineLatest({
233
+ // dataFormatter: fullDataFormatter$,
234
+ // computedData: computedData$
235
+ // }).pipe(
236
+ // takeUntil(destroy$),
237
+ // switchMap(async (d) => d),
238
+ // ).subscribe(data => {
239
+ // const groupMin = 0
240
+ // const groupMax = data.computedData[0] ? data.computedData[0].length - 1 : 0
241
+ // const groupScaleDomainMin = data.dataFormatter.groupAxis.scaleDomain[0] === 'auto'
242
+ // ? groupMin - data.dataFormatter.groupAxis.scalePadding
243
+ // : data.dataFormatter.groupAxis.scaleDomain[0] as number - data.dataFormatter.groupAxis.scalePadding
244
+ // const groupScaleDomainMax = data.dataFormatter.groupAxis.scaleDomain[1] === 'auto'
245
+ // ? groupMax + data.dataFormatter.groupAxis.scalePadding
246
+ // : data.dataFormatter.groupAxis.scaleDomain[1] as number + data.dataFormatter.groupAxis.scalePadding
247
+
248
+ // // const groupingAmount = data.computedData[0]
249
+ // // ? data.computedData[0].length
250
+ // // : 0
251
+
252
+ // let _labels = data.dataFormatter.seriesDirection === 'row'
253
+ // ? (data.computedData[0] ?? []).map(d => d.groupLabel)
254
+ // : data.computedData.map(d => d[0].groupLabel)
255
+
256
+ // const _axisLabels =
257
+ // // new Array(groupingAmount).fill(0)
258
+ // // .map((d, i) => {
259
+ // // return _labels[i] != null
260
+ // // ? _labels[i]
261
+ // // : String(i) // 沒有label則用序列號填充
262
+ // // })
263
+ // _labels
264
+ // .filter((d, i) => {
265
+ // return i >= groupScaleDomainMin && i <= groupScaleDomainMax
266
+ // })
267
+ // subscriber.next(_axisLabels)
268
+ // })
269
+ // })
270
+ const groupScaleDomain$ = combineLatest({
271
+ fullDataFormatter: fullDataFormatter$,
272
+ gridAxesSize: gridAxesSize$,
273
+ computedData: computedData$
274
+ }).pipe(
275
+ switchMap(async (d) => d),
276
+ map(data => {
277
+ const groupMin = 0
278
+ const groupMax = data.computedData[0] ? data.computedData[0].length - 1 : 0
279
+ // const groupScaleDomainMin = data.fullDataFormatter.groupAxis.scaleDomain[0] === 'auto'
280
+ // ? groupMin - data.fullDataFormatter.groupAxis.scalePadding
281
+ // : data.fullDataFormatter.groupAxis.scaleDomain[0] as number - data.fullDataFormatter.groupAxis.scalePadding
282
+ const groupScaleDomainMin = data.fullDataFormatter.groupAxis.scaleDomain[0] - data.fullDataFormatter.groupAxis.scalePadding
283
+ const groupScaleDomainMax = data.fullDataFormatter.groupAxis.scaleDomain[1] === 'max'
284
+ ? groupMax + data.fullDataFormatter.groupAxis.scalePadding
285
+ : data.fullDataFormatter.groupAxis.scaleDomain[1] as number + data.fullDataFormatter.groupAxis.scalePadding
286
+
287
+ return [groupScaleDomainMin, groupScaleDomainMax]
288
+ }),
289
+ shareReplay(1)
290
+ )
291
+
292
+ const groupLabels$ = combineLatest({
293
+ fullDataFormatter: fullDataFormatter$,
294
+ computedData: computedData$
295
+ }).pipe(
296
+ switchMap(async d => d),
297
+ map(data => {
298
+ return data.fullDataFormatter.seriesDirection === 'row'
299
+ ? (data.computedData[0] ?? []).map(d => d.groupLabel)
300
+ : data.computedData.map(d => d[0].groupLabel)
301
+ })
302
+ )
303
+
304
+ // 顯示範圍內的group labels
305
+ const scaleRangeGroupLabels$ = combineLatest({
306
+ groupScaleDomain: groupScaleDomain$,
307
+ groupLabels: groupLabels$
308
+ }).pipe(
309
+ switchMap(async d => d),
310
+ map(data => {
311
+ return data.groupLabels
312
+ .filter((d, i) => {
313
+ return i >= data.groupScaleDomain[0] && i <= data.groupScaleDomain[1]
314
+ })
315
+ })
316
+ )
317
+
318
+ const columnAmount$ = gridContainerPosition$.pipe(
319
+ map(gridContainerPosition => {
320
+ const maxColumnIndex = gridContainerPosition.reduce((acc, current) => {
321
+ return current.columnIndex > acc ? current.columnIndex : acc
322
+ }, 0)
323
+ return maxColumnIndex + 1
324
+ }),
325
+ distinctUntilChanged()
326
+ )
327
+
328
+ const rowAmount$ = gridContainerPosition$.pipe(
329
+ map(gridContainerPosition => {
330
+ const maxRowIndex = gridContainerPosition.reduce((acc, current) => {
331
+ return current.rowIndex > acc ? current.rowIndex : acc
332
+ }, 0)
333
+ return maxRowIndex + 1
334
+ }),
335
+ distinctUntilChanged()
336
+ )
337
+
338
+ return new Observable<(event: any) => { groupIndex: number; groupLabel: string }>(subscriber => {
339
+ combineLatest({
340
+ dataFormatter: fullDataFormatter$,
341
+ axisSize: gridAxesSize$,
342
+ fullChartParams: fullChartParams$,
343
+ scaleRangeGroupLabels: scaleRangeGroupLabels$,
344
+ groupLabels: groupLabels$,
345
+ groupScaleDomain: groupScaleDomain$,
346
+ columnAmount: columnAmount$,
347
+ rowAmount: rowAmount$,
348
+ layout: layout$
349
+ }).pipe(
350
+ takeUntil(destroy$),
351
+ switchMap(async (d) => d),
352
+ ).subscribe(data => {
353
+
354
+ const reverse = data.dataFormatter.valueAxis.position === 'right'
355
+ || data.dataFormatter.valueAxis.position === 'bottom'
356
+ ? true : false
357
+
358
+ // 比例尺座標對應非連續資料索引
359
+ const xIndexScale = createAxisToLabelIndexScale({
360
+ axisLabels: data.scaleRangeGroupLabels,
361
+ axisWidth: data.axisSize.width,
362
+ padding: data.dataFormatter.groupAxis.scalePadding,
363
+ reverse
364
+ })
365
+
366
+ // 依比例尺位置計算座標
367
+ const axisValuePredicate = (event: any) => {
368
+ return data.dataFormatter.groupAxis.position === 'bottom'
369
+ || data.dataFormatter.groupAxis.position === 'top'
370
+ ? event.offsetX - data.fullChartParams.padding.left
371
+ : event.offsetY - data.fullChartParams.padding.top
372
+ }
373
+
374
+ // 比例尺座標取得groupData的function
375
+ const createEventGroupData: (event: MouseEvent) => { groupIndex: number; groupLabel: string } = (event: any) => {
376
+ // 由於event座標是基於底層的,但是container會有多欄,所以要重新計算
377
+ const eventData = {
378
+ offsetX: event.offsetX * data.columnAmount % data.layout.rootWidth,
379
+ offsetY: event.offsetY * data.rowAmount % data.layout.rootHeight
380
+ }
381
+ // console.log('data.columnAmount', data.columnAmount, 'data.rowAmount', data.rowAmount, 'data.layout.rootWidth', data.layout.rootWidth, 'data.layout.rootHeight', data.layout.rootHeight)
382
+ const axisValue = axisValuePredicate(eventData)
383
+ const xIndex = xIndexScale(axisValue)
384
+ const currentxIndexStart = Math.ceil(data.groupScaleDomain[0]) // 因為有padding所以會有小數點,所以要無條件進位
385
+ const groupIndex = xIndex + currentxIndexStart
386
+
387
+ return {
388
+ groupIndex,
389
+ groupLabel: data.groupLabels[groupIndex] ?? ''
390
+ }
391
+ }
392
+
393
+ subscriber.next(createEventGroupData)
394
+
395
+ return function unsubscribe () {
396
+ destroy$.next(undefined)
397
+ }
398
+ })
399
+ })
400
+ }
401
+
402
+ export const gridGroupPositionObservable = ({ rootSelection, fullDataFormatter$, gridAxesSize$, computedData$, fullChartParams$, gridContainerPosition$, layout$ }: {
403
+ rootSelection: d3.Selection<any, unknown, any, unknown>
404
+ fullDataFormatter$: Observable<DataFormatterGrid>
405
+ gridAxesSize$: Observable<{
406
+ width: number;
407
+ height: number;
408
+ }>
409
+ computedData$: Observable<ComputedDataGrid>
410
+ fullChartParams$: Observable<ChartParams>
411
+ gridContainerPosition$: Observable<ContainerPositionScaled[]>
412
+ layout$: Observable<Layout>
413
+ }) => {
414
+ const rootMousemove$ = d3EventObservable(rootSelection, 'mousemove')
415
+
416
+ const groupScaleDomain$ = combineLatest({
417
+ fullDataFormatter: fullDataFormatter$,
418
+ gridAxesSize: gridAxesSize$,
419
+ computedData: computedData$
420
+ }).pipe(
421
+ switchMap(async (d) => d),
422
+ map(data => {
423
+ const groupMin = 0
424
+ const groupMax = data.computedData[0] ? data.computedData[0].length - 1 : 0
425
+ // const groupScaleDomainMin = data.fullDataFormatter.groupAxis.scaleDomain[0] === 'auto'
426
+ // ? groupMin - data.fullDataFormatter.groupAxis.scalePadding
427
+ // : data.fullDataFormatter.groupAxis.scaleDomain[0] as number - data.fullDataFormatter.groupAxis.scalePadding
428
+ const groupScaleDomainMin = data.fullDataFormatter.groupAxis.scaleDomain[0] - data.fullDataFormatter.groupAxis.scalePadding
429
+ const groupScaleDomainMax = data.fullDataFormatter.groupAxis.scaleDomain[1] === 'max'
430
+ ? groupMax + data.fullDataFormatter.groupAxis.scalePadding
431
+ : data.fullDataFormatter.groupAxis.scaleDomain[1] as number + data.fullDataFormatter.groupAxis.scalePadding
432
+
433
+ return [groupScaleDomainMin, groupScaleDomainMax]
434
+ }),
435
+ shareReplay(1)
436
+ )
437
+
438
+ const groupLabels$ = combineLatest({
439
+ fullDataFormatter: fullDataFormatter$,
440
+ computedData: computedData$
441
+ }).pipe(
442
+ switchMap(async d => d),
443
+ map(data => {
444
+ return data.fullDataFormatter.seriesDirection === 'row'
445
+ ? (data.computedData[0] ?? []).map(d => d.groupLabel)
446
+ : data.computedData.map(d => d[0].groupLabel)
447
+ })
448
+ )
449
+
450
+ const scaleRangeGroupLabels$ = combineLatest({
451
+ groupScaleDomain: groupScaleDomain$,
452
+ groupLabels: groupLabels$
453
+ }).pipe(
454
+ switchMap(async d => d),
455
+ map(data => {
456
+ return data.groupLabels
457
+ .filter((d, i) => {
458
+ return i >= data.groupScaleDomain[0] && i <= data.groupScaleDomain[1]
459
+ })
460
+ })
461
+ )
462
+
463
+ const reverse$ = fullDataFormatter$.pipe(
464
+ map(d => {
465
+ return d.valueAxis.position === 'right' || d.valueAxis.position === 'bottom'
466
+ ? true
467
+ : false
468
+ })
469
+ )
470
+
471
+ // 比例尺座標對應非連續資料索引
472
+ const xIndexScale$ = combineLatest({
473
+ reverse: reverse$,
474
+ gridAxesSize: gridAxesSize$,
475
+ scaleRangeGroupLabels: scaleRangeGroupLabels$,
476
+ fullDataFormatter: fullDataFormatter$
477
+ }).pipe(
478
+ switchMap(async d => d),
479
+ map(data => {
480
+ return createAxisToLabelIndexScale({
481
+ axisLabels: data.scaleRangeGroupLabels,
482
+ axisWidth: data.gridAxesSize.width,
483
+ padding: data.fullDataFormatter.groupAxis.scalePadding,
484
+ reverse: data.reverse
485
+ })
486
+ })
487
+ )
488
+
489
+ const columnAmount$ = gridContainerPosition$.pipe(
490
+ map(gridContainerPosition => {
491
+ const maxColumnIndex = gridContainerPosition.reduce((acc, current) => {
492
+ return current.columnIndex > acc ? current.columnIndex : acc
493
+ }, 0)
494
+ return maxColumnIndex + 1
495
+ }),
496
+ distinctUntilChanged()
497
+ )
498
+
499
+ const rowAmount$ = gridContainerPosition$.pipe(
500
+ map(gridContainerPosition => {
501
+ const maxRowIndex = gridContainerPosition.reduce((acc, current) => {
502
+ return current.rowIndex > acc ? current.rowIndex : acc
503
+ }, 0)
504
+ return maxRowIndex + 1
505
+ }),
506
+ distinctUntilChanged()
507
+ )
508
+
509
+ const axisValue$ = combineLatest({
510
+ fullDataFormatter: fullDataFormatter$,
511
+ fullChartParams: fullChartParams$,
512
+ rootMousemove: rootMousemove$,
513
+ columnAmount: columnAmount$,
514
+ rowAmount: rowAmount$,
515
+ layout: layout$
516
+ }).pipe(
517
+ switchMap(async d => d),
518
+ map(data => {
519
+ // 由於event座標是基於底層的,但是container會有多欄,所以要重新計算
520
+ const eventData = {
521
+ offsetX: data.rootMousemove.offsetX * data.columnAmount % data.layout.rootWidth,
522
+ offsetY: data.rootMousemove.offsetY * data.rowAmount % data.layout.rootHeight
523
+ }
524
+ return data.fullDataFormatter.groupAxis.position === 'bottom'
525
+ || data.fullDataFormatter.groupAxis.position === 'top'
526
+ ? eventData.offsetX - data.fullChartParams.padding.left
527
+ : eventData.offsetY - data.fullChartParams.padding.top
528
+ })
529
+ )
530
+
531
+ const groupIndex$ = combineLatest({
532
+ xIndexScale: xIndexScale$,
533
+ axisValue: axisValue$,
534
+ groupScaleDomain: groupScaleDomain$
535
+ }).pipe(
536
+ switchMap(async d => d),
537
+ map(data => {
538
+ const xIndex = data.xIndexScale(data.axisValue)
539
+ const currentxIndexStart = Math.ceil(data.groupScaleDomain[0]) // 因為有padding所以會有小數點,所以要無條件進位
540
+ return xIndex + currentxIndexStart
541
+ })
542
+ )
543
+
544
+ const groupLabel$ = combineLatest({
545
+ groupIndex: groupIndex$,
546
+ groupLabels: groupLabels$
547
+ }).pipe(
548
+ switchMap(async d => d),
549
+ map(data => {
550
+ return data.groupLabels[data.groupIndex] ?? ''
551
+ })
552
+ )
553
+
554
+ return combineLatest({
555
+ groupIndex: groupIndex$,
556
+ groupLabel: groupLabel$
557
+ }).pipe(
558
+ switchMap(async d => d),
559
+ map(data => {
560
+ return {
561
+ groupIndex: data.groupIndex,
562
+ groupLabel: data.groupLabel
563
+ }
564
+ })
565
+ )
566
+ }
567
+
568
+ // const gridContainerEventData$ = ({ eventData$, gridContainerPosition$, layout$ }: {
569
+ // eventData$: Observable<any>
570
+ // gridContainerPosition$: Observable<ContainerPositionScaled[]>
571
+ // layout$: Observable<Layout>
572
+ // }): Observable<{
573
+ // offsetX: number;
574
+ // offsetY: number;
575
+ // }> => {
576
+ // const columnAmount$ = gridContainerPosition$.pipe(
577
+ // map(gridContainerPosition => {
578
+ // const maxColumnIndex = gridContainerPosition.reduce((acc, current) => {
579
+ // return current.columnIndex > acc ? current.columnIndex : acc
580
+ // }, 0)
581
+ // return maxColumnIndex + 1
582
+ // }),
583
+ // distinctUntilChanged()
584
+ // )
585
+
586
+ // const rowAmount$ = gridContainerPosition$.pipe(
587
+ // map(gridContainerPosition => {
588
+ // const maxRowIndex = gridContainerPosition.reduce((acc, current) => {
589
+ // return current.rowIndex > acc ? current.rowIndex : acc
590
+ // }, 0)
591
+ // return maxRowIndex + 1
592
+ // }),
593
+ // distinctUntilChanged()
594
+ // )
595
+
596
+ // return combineLatest({
597
+ // eventData: eventData$,
598
+ // gridContainerPosition: gridContainerPosition$,
599
+ // layout: layout$,
600
+ // columnAmount: columnAmount$,
601
+ // rowAmount: rowAmount$
602
+ // }).pipe(
603
+ // switchMap(async d => d),
604
+ // map(data => {
605
+ // // 由於event座標是基於底層的,但是container會有多欄,所以要重新計算
606
+ // const eventData = {
607
+ // offsetX: data.eventData.offsetX * data.columnAmount % data.layout.rootWidth,
608
+ // offsetY: data.eventData.offsetY * data.rowAmount % data.layout.rootHeight
609
+ // }
610
+ // return eventData
611
+ // })
612
+ // )
555
613
  // }