@orbcharts/core 3.0.0-alpha.61 → 3.0.0-alpha.63

Sign up to get free protection for your applications and to get access to all the features.
Files changed (215) hide show
  1. package/LICENSE +200 -200
  2. package/dist/lib/core-types.d.ts +1 -0
  3. package/dist/orbcharts-core.es.js +3418 -2726
  4. package/dist/orbcharts-core.umd.js +6 -2
  5. package/dist/src/AbstractChart.d.ts +5 -3
  6. package/dist/src/GridChart.d.ts +1 -1
  7. package/dist/src/MultiGridChart.d.ts +1 -1
  8. package/dist/src/MultiValueChart.d.ts +1 -1
  9. package/dist/src/RelationshipChart.d.ts +1 -1
  10. package/dist/src/SeriesChart.d.ts +1 -1
  11. package/dist/src/TreeChart.d.ts +1 -1
  12. package/dist/src/base/createBaseChart.d.ts +1 -1
  13. package/dist/src/base/createBasePlugin.d.ts +1 -1
  14. package/dist/src/base/validators/chartOptionsValidator.d.ts +3 -0
  15. package/dist/src/base/validators/chartParamsValidator.d.ts +3 -0
  16. package/dist/src/base/validators/elementValidator.d.ts +3 -0
  17. package/dist/src/base/validators/pluginsValidator.d.ts +3 -0
  18. package/dist/src/defaults.d.ts +1 -16
  19. package/dist/src/defineGridPlugin.d.ts +1 -1
  20. package/dist/src/defineMultiGridPlugin.d.ts +1 -1
  21. package/dist/src/defineMultiValuePlugin.d.ts +1 -1
  22. package/dist/src/defineNoneDataPlugin.d.ts +1 -1
  23. package/dist/src/defineRelationshipPlugin.d.ts +1 -1
  24. package/dist/src/defineSeriesPlugin.d.ts +1 -1
  25. package/dist/src/defineTreePlugin.d.ts +1 -1
  26. package/dist/src/grid/computedDataFn.d.ts +4 -0
  27. package/dist/src/grid/contextObserverCallback.d.ts +3 -0
  28. package/dist/src/grid/dataFormatterValidator.d.ts +3 -0
  29. package/dist/src/grid/dataValidator.d.ts +3 -0
  30. package/dist/src/index.d.ts +1 -1
  31. package/dist/src/multiGrid/computedDataFn.d.ts +3 -0
  32. package/dist/src/multiGrid/contextObserverCallback.d.ts +3 -0
  33. package/dist/src/multiGrid/dataFormatterValidator.d.ts +3 -0
  34. package/dist/src/multiGrid/dataValidator.d.ts +3 -0
  35. package/dist/src/multiValue/computedDataFn.d.ts +3 -0
  36. package/dist/src/multiValue/contextObserverCallback.d.ts +3 -0
  37. package/dist/src/multiValue/dataFormatterValidator.d.ts +3 -0
  38. package/dist/src/multiValue/dataValidator.d.ts +3 -0
  39. package/dist/src/relationship/computedDataFn.d.ts +3 -0
  40. package/dist/src/relationship/contextObserverCallback.d.ts +3 -0
  41. package/dist/src/relationship/dataFormatterValidator.d.ts +3 -0
  42. package/dist/src/relationship/dataValidator.d.ts +3 -0
  43. package/dist/src/series/computedDataFn.d.ts +3 -0
  44. package/dist/src/series/contextObserverCallback.d.ts +3 -0
  45. package/dist/src/series/dataFormatterValidator.d.ts +3 -0
  46. package/dist/src/series/dataValidator.d.ts +3 -0
  47. package/dist/src/tree/computedDataFn.d.ts +3 -0
  48. package/dist/src/tree/contextObserverCallback.d.ts +3 -0
  49. package/dist/src/tree/dataFormatterValidator.d.ts +3 -0
  50. package/dist/src/tree/dataValidator.d.ts +3 -0
  51. package/dist/src/utils/commonUtils.d.ts +1 -0
  52. package/dist/src/utils/errorMessage.d.ts +14 -0
  53. package/dist/src/{grid → utils}/gridObservables.d.ts +2 -2
  54. package/dist/src/utils/index.d.ts +7 -3
  55. package/dist/src/{multiGrid → utils}/multiGridObservables.d.ts +1 -1
  56. package/dist/src/utils/observables.d.ts +2 -1
  57. package/dist/src/utils/orbchartsUtils.d.ts +1 -12
  58. package/dist/src/{series → utils}/seriesObservables.d.ts +3 -3
  59. package/dist/src/{tree → utils}/treeObservables.d.ts +1 -1
  60. package/dist/src/utils/validator.d.ts +3 -0
  61. package/lib/core-types.ts +7 -0
  62. package/package.json +42 -41
  63. package/src/AbstractChart.ts +57 -48
  64. package/src/GridChart.ts +24 -20
  65. package/src/MultiGridChart.ts +24 -20
  66. package/src/MultiValueChart.ts +24 -20
  67. package/src/RelationshipChart.ts +24 -20
  68. package/src/SeriesChart.ts +24 -20
  69. package/src/TreeChart.ts +24 -20
  70. package/src/base/createBaseChart.ts +500 -388
  71. package/src/base/createBasePlugin.ts +152 -95
  72. package/src/base/validators/chartOptionsValidator.ts +24 -0
  73. package/src/base/validators/chartParamsValidator.ts +134 -0
  74. package/src/base/validators/elementValidator.ts +14 -0
  75. package/src/base/validators/pluginsValidator.ts +15 -0
  76. package/src/defaults.ts +232 -228
  77. package/src/defineGridPlugin.ts +3 -3
  78. package/src/defineMultiGridPlugin.ts +3 -3
  79. package/src/defineMultiValuePlugin.ts +3 -3
  80. package/src/defineNoneDataPlugin.ts +4 -4
  81. package/src/defineRelationshipPlugin.ts +3 -3
  82. package/src/defineSeriesPlugin.ts +3 -3
  83. package/src/defineTreePlugin.ts +3 -3
  84. package/src/grid/{computeGridData.ts → computedDataFn.ts} +129 -134
  85. package/src/grid/{createGridContextObserver.ts → contextObserverCallback.ts} +155 -155
  86. package/src/grid/dataFormatterValidator.ts +102 -0
  87. package/src/grid/dataValidator.ts +13 -0
  88. package/src/index.ts +20 -21
  89. package/src/multiGrid/{computeMultiGridData.ts → computedDataFn.ts} +123 -130
  90. package/src/multiGrid/{createMultiGridContextObserver.ts → contextObserverCallback.ts} +41 -41
  91. package/src/multiGrid/dataFormatterValidator.ts +116 -0
  92. package/src/multiGrid/dataValidator.ts +13 -0
  93. package/src/multiValue/{computeMultiValueData.ts → computedDataFn.ts} +176 -179
  94. package/src/multiValue/{createMultiValueContextObserver.ts → contextObserverCallback.ts} +12 -12
  95. package/src/multiValue/dataFormatterValidator.ts +10 -0
  96. package/src/multiValue/dataValidator.ts +10 -0
  97. package/src/relationship/{computeRelationshipData.ts → computedDataFn.ts} +125 -118
  98. package/src/relationship/{createRelationshipContextObserver.ts → contextObserverCallback.ts} +12 -12
  99. package/src/relationship/dataFormatterValidator.ts +10 -0
  100. package/src/relationship/dataValidator.ts +10 -0
  101. package/src/series/{computeSeriesData.ts → computedDataFn.ts} +88 -90
  102. package/src/series/{createSeriesContextObserver.ts → contextObserverCallback.ts} +100 -93
  103. package/src/series/dataFormatterValidator.ts +42 -0
  104. package/src/series/dataValidator.ts +13 -0
  105. package/src/tree/{computeTreeData.ts → computedDataFn.ts} +130 -132
  106. package/src/tree/{createTreeContextObserver.ts → contextObserverCallback.ts} +61 -61
  107. package/src/tree/dataFormatterValidator.ts +14 -0
  108. package/src/tree/dataValidator.ts +14 -0
  109. package/src/utils/commonUtils.ts +54 -50
  110. package/src/utils/d3Utils.ts +108 -108
  111. package/src/utils/errorMessage.ts +43 -0
  112. package/src/{grid → utils}/gridObservables.ts +611 -614
  113. package/src/utils/index.ts +10 -4
  114. package/src/{multiGrid → utils}/multiGridObservables.ts +366 -365
  115. package/src/utils/observables.ts +219 -202
  116. package/src/utils/orbchartsUtils.ts +352 -349
  117. package/src/{series → utils}/seriesObservables.ts +175 -175
  118. package/src/{tree → utils}/treeObservables.ts +94 -94
  119. package/src/utils/validator.ts +126 -0
  120. package/tsconfig.base.json +13 -13
  121. package/tsconfig.json +2 -2
  122. package/vite-env.d.ts +7 -0
  123. package/vite.config.js +22 -22
  124. package/dist/src/grid/computeGridData.d.ts +0 -6
  125. package/dist/src/grid/createGridContextObserver.d.ts +0 -3
  126. package/dist/src/multiGrid/computeMultiGridData.d.ts +0 -3
  127. package/dist/src/multiGrid/createMultiGridContextObserver.d.ts +0 -3
  128. package/dist/src/multiValue/computeMultiValueData.d.ts +0 -3
  129. package/dist/src/multiValue/createMultiValueContextObserver.d.ts +0 -3
  130. package/dist/src/relationship/computeRelationshipData.d.ts +0 -3
  131. package/dist/src/relationship/createRelationshipContextObserver.d.ts +0 -3
  132. package/dist/src/series/computeSeriesData.d.ts +0 -3
  133. package/dist/src/series/createSeriesContextObserver.d.ts +0 -3
  134. package/dist/src/tree/computeTreeData.d.ts +0 -3
  135. package/dist/src/tree/createTreeContextObserver.d.ts +0 -3
  136. package/dist/src/types/Axis.d.ts +0 -1
  137. package/dist/src/types/Chart.d.ts +0 -45
  138. package/dist/src/types/ChartParams.d.ts +0 -36
  139. package/dist/src/types/ComputedData.d.ts +0 -42
  140. package/dist/src/types/ComputedDataGrid.d.ts +0 -5
  141. package/dist/src/types/ComputedDataMultiGrid.d.ts +0 -3
  142. package/dist/src/types/ComputedDataMultiValue.d.ts +0 -6
  143. package/dist/src/types/ComputedDataRelationship.d.ts +0 -18
  144. package/dist/src/types/ComputedDataSeries.d.ts +0 -5
  145. package/dist/src/types/ComputedDataTree.d.ts +0 -7
  146. package/dist/src/types/ContextObserver.d.ts +0 -28
  147. package/dist/src/types/ContextObserverGrid.d.ts +0 -41
  148. package/dist/src/types/ContextObserverMultiGrid.d.ts +0 -15
  149. package/dist/src/types/ContextObserverMultiValue.d.ts +0 -4
  150. package/dist/src/types/ContextObserverRelationship.d.ts +0 -4
  151. package/dist/src/types/ContextObserverSeries.d.ts +0 -27
  152. package/dist/src/types/ContextObserverTree.d.ts +0 -11
  153. package/dist/src/types/ContextSubject.d.ts +0 -15
  154. package/dist/src/types/Data.d.ts +0 -19
  155. package/dist/src/types/DataFormatter.d.ts +0 -41
  156. package/dist/src/types/DataFormatterGrid.d.ts +0 -34
  157. package/dist/src/types/DataFormatterMultiGrid.d.ts +0 -20
  158. package/dist/src/types/DataFormatterMultiValue.d.ts +0 -18
  159. package/dist/src/types/DataFormatterRelationship.d.ts +0 -10
  160. package/dist/src/types/DataFormatterSeries.d.ts +0 -19
  161. package/dist/src/types/DataFormatterTree.d.ts +0 -7
  162. package/dist/src/types/DataGrid.d.ts +0 -6
  163. package/dist/src/types/DataMultiGrid.d.ts +0 -6
  164. package/dist/src/types/DataMultiValue.d.ts +0 -7
  165. package/dist/src/types/DataRelationship.d.ts +0 -21
  166. package/dist/src/types/DataSeries.d.ts +0 -6
  167. package/dist/src/types/DataTree.d.ts +0 -15
  168. package/dist/src/types/Event.d.ts +0 -56
  169. package/dist/src/types/Layout.d.ts +0 -8
  170. package/dist/src/types/Padding.d.ts +0 -6
  171. package/dist/src/types/Plugin.d.ts +0 -37
  172. package/dist/src/types/TransformData.d.ts +0 -8
  173. package/dist/src/types/index.d.ts +0 -37
  174. package/src/types/Axis.ts +0 -1
  175. package/src/types/Chart.ts +0 -54
  176. package/src/types/ChartParams.ts +0 -51
  177. package/src/types/ComputedData.ts +0 -84
  178. package/src/types/ComputedDataGrid.ts +0 -14
  179. package/src/types/ComputedDataMultiGrid.ts +0 -3
  180. package/src/types/ComputedDataMultiValue.ts +0 -9
  181. package/src/types/ComputedDataRelationship.ts +0 -20
  182. package/src/types/ComputedDataSeries.ts +0 -8
  183. package/src/types/ComputedDataTree.ts +0 -20
  184. package/src/types/ContextObserver.ts +0 -38
  185. package/src/types/ContextObserverGrid.ts +0 -43
  186. package/src/types/ContextObserverMultiGrid.ts +0 -17
  187. package/src/types/ContextObserverMultiValue.ts +0 -5
  188. package/src/types/ContextObserverRelationship.ts +0 -5
  189. package/src/types/ContextObserverSeries.ts +0 -29
  190. package/src/types/ContextObserverTree.ts +0 -11
  191. package/src/types/ContextSubject.ts +0 -18
  192. package/src/types/Data.ts +0 -45
  193. package/src/types/DataFormatter.ts +0 -74
  194. package/src/types/DataFormatterGrid.ts +0 -68
  195. package/src/types/DataFormatterMultiGrid.ts +0 -45
  196. package/src/types/DataFormatterMultiValue.ts +0 -24
  197. package/src/types/DataFormatterRelationship.ts +0 -26
  198. package/src/types/DataFormatterSeries.ts +0 -20
  199. package/src/types/DataFormatterTree.ts +0 -12
  200. package/src/types/DataGrid.ts +0 -11
  201. package/src/types/DataMultiGrid.ts +0 -7
  202. package/src/types/DataMultiValue.ts +0 -12
  203. package/src/types/DataRelationship.ts +0 -28
  204. package/src/types/DataSeries.ts +0 -11
  205. package/src/types/DataTree.ts +0 -20
  206. package/src/types/Event.ts +0 -153
  207. package/src/types/Layout.ts +0 -12
  208. package/src/types/Padding.ts +0 -6
  209. package/src/types/Plugin.ts +0 -60
  210. package/src/types/TransformData.ts +0 -8
  211. package/src/types/index.ts +0 -37
  212. /package/dist/src/{multiValue → utils}/multiValueObservables.d.ts +0 -0
  213. /package/dist/src/{relationship → utils}/relationshipObservables.d.ts +0 -0
  214. /package/src/{multiValue → utils}/multiValueObservables.ts +0 -0
  215. /package/src/{relationship → utils}/relationshipObservables.ts +0 -0
@@ -1,615 +1,612 @@
1
- import {
2
- combineLatest,
3
- distinctUntilChanged,
4
- iif,
5
- filter,
6
- map,
7
- merge,
8
- takeUntil,
9
- shareReplay,
10
- switchMap,
11
- Subject,
12
- Observable } from 'rxjs'
13
- import type {
14
- AxisPosition,
15
- ChartType,
16
- ChartParams,
17
- ComputedDataTypeMap,
18
- ComputedDatumTypeMap,
19
- ComputedDataGrid,
20
- ContextObserverFn,
21
- DataTypeMap,
22
- DataGridDatum,
23
- ComputedDatumGrid,
24
- DataFormatterTypeMap,
25
- DataFormatterGrid,
26
- DataFormatterValueAxis,
27
- DataFormatterGroupAxis,
28
- ComputedLayoutDatumGrid,
29
- ComputedLayoutDataGrid,
30
- GridContainerPosition,
31
- HighlightTarget,
32
- Layout,
33
- TransformData } from '../types'
34
- import { getMinAndMaxGrid } from '../utils/orbchartsUtils'
35
- import { createAxisLinearScale, createAxisPointScale, createAxisQuantizeScale } from '../utils/d3Utils'
36
- import { highlightObservable } from '../utils/observables'
37
- import { calcGridContainerLayout } from '../utils/orbchartsUtils'
38
- import { DATA_FORMATTER_GRID_GRID_DEFAULT } from '../defaults'
39
- import { getMinAndMaxValue, transposeData, createGridSeriesLabels, createGridGroupLabels, seriesColorPredicate } from '../utils/orbchartsUtils'
40
-
41
- export const gridComputedLayoutDataObservable = ({ computedData$, fullDataFormatter$, layout$ }: {
42
- computedData$: Observable<ComputedDataTypeMap<'grid'>>
43
- fullDataFormatter$: Observable<DataFormatterTypeMap<'grid'>>
44
- layout$: Observable<Layout>
45
- }): Observable<ComputedLayoutDatumGrid[][]> => {
46
-
47
- // 未篩選group範圍前的group scale
48
- function createOriginGroupScale (computedData: ComputedDatumGrid[][], dataFormatter: DataFormatterGrid, layout: Layout) {
49
- const groupAxisWidth = (dataFormatter.grid.groupAxis.position === 'top' || dataFormatter.grid.groupAxis.position === 'bottom')
50
- ? layout.width
51
- : layout.height
52
- const groupEndIndex = computedData[0] ? computedData[0].length - 1 : 0
53
- const groupScale: d3.ScaleLinear<number, number> = createAxisLinearScale({
54
- maxValue: groupEndIndex,
55
- minValue: 0,
56
- axisWidth: groupAxisWidth,
57
- scaleDomain: [0, groupEndIndex], // 不使用dataFormatter設定
58
- scaleRange: [0, 1] // 不使用dataFormatter設定
59
- })
60
- return groupScale
61
- }
62
-
63
- // 未篩選group範圍及visible前的value scale
64
- function createOriginValueScale (computedData: ComputedDatumGrid[][], dataFormatter: DataFormatterGrid, layout: Layout) {
65
- const valueAxisWidth = (dataFormatter.grid.valueAxis.position === 'left' || dataFormatter.grid.valueAxis.position === 'right')
66
- ? layout.height
67
- : layout.width
68
-
69
- const listData = computedData.flat()
70
- const [minValue, maxValue] = getMinAndMaxValue(listData)
71
-
72
- const valueScale: d3.ScaleLinear<number, number> = createAxisLinearScale({
73
- maxValue,
74
- minValue,
75
- axisWidth: valueAxisWidth,
76
- scaleDomain: [minValue, maxValue], // 不使用dataFormatter設定
77
- scaleRange: [0, 1] // 不使用dataFormatter設定
78
- })
79
-
80
- return valueScale
81
- }
82
-
83
- return combineLatest({
84
- computedData: computedData$,
85
- fullDataFormatter: fullDataFormatter$,
86
- layout: layout$
87
- }).pipe(
88
- switchMap(async d => d),
89
- map(data => {
90
- const groupScale = createOriginGroupScale(data.computedData, data.fullDataFormatter, data.layout)
91
- const valueScale = createOriginValueScale(data.computedData, data.fullDataFormatter, data.layout)
92
- const zeroY = valueScale(0)
93
-
94
- return data.computedData.map((seriesData, seriesIndex) => {
95
- return seriesData.map((groupDatum, groupIndex) => {
96
- const axisX = groupScale(groupIndex)
97
- const axisY = valueScale(groupDatum.value ?? 0)
98
- return {
99
- ...groupDatum,
100
- axisX,
101
- axisY,
102
- axisYFromZero: axisY - zeroY
103
- }
104
- })
105
- })
106
- })
107
- )
108
- }
109
-
110
- export const gridAxesTransformObservable = ({ fullDataFormatter$, layout$ }: {
111
- fullDataFormatter$: Observable<DataFormatterTypeMap<'grid'>>
112
- layout$: Observable<Layout>
113
- }): Observable<TransformData> => {
114
- const destroy$ = new Subject()
115
-
116
- function calcAxesTransform ({ xAxis, yAxis, width, height }: {
117
- xAxis: DataFormatterGroupAxis | DataFormatterValueAxis,
118
- yAxis: DataFormatterValueAxis,
119
- width: number,
120
- height: number
121
- }): TransformData {
122
- if (!xAxis || !yAxis) {
123
- return {
124
- translate: [0, 0],
125
- scale: [1, 1],
126
- rotate: 0,
127
- rotateX: 0,
128
- rotateY: 0,
129
- value: ''
130
- }
131
- }
132
- // const width = size.width - fullChartParams.layout.left - fullChartParams.layout.right
133
- // const height = size.height - fullChartParams.layout.top - fullChartParams.layout.bottom
134
- let translateX = 0
135
- let translateY = 0
136
- let rotate = 0
137
- let rotateX = 0
138
- let rotateY = 0
139
- if (xAxis.position === 'bottom') {
140
- if (yAxis.position === 'left') {
141
- rotateX = 180
142
- translateY = height
143
- } else if (yAxis.position === 'right') {
144
- rotateX = 180
145
- rotateY = 180
146
- translateX = width
147
- translateY = height
148
- } else {
149
- // 預設
150
- rotateX = 180
151
- translateY = height
152
- }
153
- } else if (xAxis.position === 'top') {
154
- if (yAxis.position === 'left') {
155
- } else if (yAxis.position === 'right') {
156
- rotateY = 180
157
- translateX = width
158
- } else {
159
- // 預設
160
- rotateX = 180
161
- translateY = height
162
- }
163
- } else if (xAxis.position === 'left') {
164
- if (yAxis.position === 'bottom') {
165
- rotate = -90
166
- translateY = height
167
- } else if (yAxis.position === 'top') {
168
- rotate = -90
169
- rotateY = 180
170
- } else {
171
- // 預設
172
- rotateX = 180
173
- translateY = height
174
- }
175
- } else if (xAxis.position === 'right') {
176
- if (yAxis.position === 'bottom') {
177
- rotate = -90
178
- rotateX = 180
179
- translateY = height
180
- translateX = width
181
- } else if (yAxis.position === 'top') {
182
- rotate = -90
183
- rotateX = 180
184
- rotateY = 180
185
- translateX = width
186
- } else {
187
- // 預設
188
- rotateX = 180
189
- translateY = height
190
- }
191
- } else {
192
- // 預設
193
- rotateX = 180
194
- translateY = height
195
- }
196
- // selection.style('transform', `translate(${translateX}px, ${translateY}px) rotate(${rotate}deg) rotateX(${rotateX}deg) rotateY(${rotateY}deg)`)
197
-
198
- return {
199
- translate: [translateX, translateY],
200
- scale: [1, 1],
201
- rotate,
202
- rotateX,
203
- rotateY,
204
- value: `translate(${translateX}px, ${translateY}px) rotate(${rotate}deg) rotateX(${rotateX}deg) rotateY(${rotateY}deg)`
205
- }
206
- }
207
-
208
- return new Observable(subscriber => {
209
- combineLatest({
210
- fullDataFormatter: fullDataFormatter$,
211
- layout: layout$
212
- }).pipe(
213
- takeUntil(destroy$),
214
- switchMap(async (d) => d),
215
- ).subscribe(data => {
216
- const axesTransformData = calcAxesTransform({
217
- xAxis: data.fullDataFormatter.grid.groupAxis,
218
- yAxis: data.fullDataFormatter.grid.valueAxis,
219
- width: data.layout.width,
220
- height: data.layout.height
221
- })
222
-
223
- subscriber.next(axesTransformData)
224
- })
225
-
226
- return function unscbscribe () {
227
- destroy$.next(undefined)
228
- }
229
- })
230
- }
231
-
232
-
233
- export const gridAxesReverseTransformObservable = ({ gridAxesTransform$ }: {
234
- gridAxesTransform$: Observable<TransformData>
235
- }): Observable<TransformData> => {
236
- return gridAxesTransform$.pipe(
237
- map(d => {
238
- // const translate: [number, number] = [d.translate[0] * -1, d.translate[1] * -1]
239
- const translate: [number, number] = [0, 0] // 無需逆轉
240
- const scale: [number, number] = [1 / d.scale[0], 1 / d.scale[1]]
241
- const rotate = d.rotate * -1
242
- const rotateX = d.rotateX * -1
243
- const rotateY = d.rotateY * -1
244
- return {
245
- translate,
246
- scale,
247
- rotate,
248
- rotateX,
249
- rotateY,
250
- value: `translate(${translate[0]}px, ${translate[1]}px) rotateX(${rotateX}deg) rotateY(${rotateY}deg) rotate(${rotate}deg)`
251
- }
252
- }),
253
- )
254
- }
255
-
256
- export const gridGraphicTransformObservable = ({ computedData$, fullDataFormatter$, layout$ }: {
257
- computedData$: Observable<ComputedDataTypeMap<'grid'>>
258
- fullDataFormatter$: Observable<DataFormatterTypeMap<'grid'>>
259
- layout$: Observable<Layout>
260
- }): Observable<TransformData> => {
261
- const destroy$ = new Subject()
262
-
263
- function calcGridDataAreaTransform ({ data, groupAxis, valueAxis, width, height }: {
264
- data: ComputedDataTypeMap<'grid'>
265
- groupAxis: DataFormatterGroupAxis
266
- valueAxis: DataFormatterValueAxis
267
- width: number
268
- height: number
269
- }): TransformData {
270
- let translateX = 0
271
- let translateY = 0
272
- let scaleX = 0
273
- let scaleY = 0
274
-
275
- // -- groupScale --
276
- const groupAxisWidth = (groupAxis.position === 'top' || groupAxis.position === 'bottom')
277
- ? width
278
- : height
279
- const groupMin = 0
280
- const groupMax = data[0] ? data[0].length - 1 : 0
281
- // const groupScaleDomainMin = groupAxis.scaleDomain[0] === 'min'
282
- // ? groupMin - groupAxis.scalePadding
283
- // : groupAxis.scaleDomain[0] as number - groupAxis.scalePadding
284
- const groupScaleDomainMin = groupAxis.scaleDomain[0] - groupAxis.scalePadding
285
- const groupScaleDomainMax = groupAxis.scaleDomain[1] === 'max'
286
- ? groupMax + groupAxis.scalePadding
287
- : groupAxis.scaleDomain[1] as number + groupAxis.scalePadding
288
-
289
- const groupScale: d3.ScaleLinear<number, number> = createAxisLinearScale({
290
- maxValue: groupMax,
291
- minValue: groupMin,
292
- axisWidth: groupAxisWidth,
293
- // scaleDomain: groupAxis.scaleDomain,
294
- scaleDomain: [groupScaleDomainMin, groupScaleDomainMax],
295
- scaleRange: [0, 1]
296
- })
297
-
298
- // -- translateX, scaleX --
299
- const rangeMinX = groupScale(groupMin)
300
- const rangeMaxX = groupScale(groupMax)
301
- if (groupMin == groupMax) {
302
- // 當group只有一個
303
- translateX = 0
304
- scaleX = 1
305
- } else {
306
- translateX = rangeMinX
307
- const gWidth = rangeMaxX - rangeMinX
308
- scaleX = gWidth / groupAxisWidth
309
- }
310
-
311
- // -- valueScale --
312
- const filteredData = data.map((d, i) => {
313
- return d.filter((_d, _i) => {
314
- return _i >= groupScaleDomainMin && _i <= groupScaleDomainMax && _d.visible == true
315
- })
316
- })
317
-
318
- const filteredMinAndMax = getMinAndMaxGrid(filteredData)
319
- if (filteredMinAndMax[0] === filteredMinAndMax[1]) {
320
- filteredMinAndMax[0] = filteredMinAndMax[1] - 1 // 避免最大及最小值相同造成無法計算scale
321
- }
322
-
323
- const valueAxisWidth = (valueAxis.position === 'left' || valueAxis.position === 'right')
324
- ? height
325
- : width
326
-
327
- const valueScale: d3.ScaleLinear<number, number> = createAxisLinearScale({
328
- maxValue: filteredMinAndMax[1],
329
- minValue: filteredMinAndMax[0],
330
- axisWidth: valueAxisWidth,
331
- scaleDomain: valueAxis.scaleDomain,
332
- scaleRange: valueAxis.scaleRange
333
- })
334
-
335
- // -- translateY, scaleY --
336
- const minAndMax = getMinAndMaxGrid(data)
337
- if (minAndMax[0] === minAndMax[1]) {
338
- minAndMax[0] = minAndMax[1] - 1 // 避免最大及最小值相同造成無法計算scale
339
- }
340
- const rangeMinY = valueScale(minAndMax[0])
341
- const rangeMaxY = valueScale(minAndMax[1])
342
- translateY = rangeMinY
343
- const gHeight = rangeMaxY - rangeMinY
344
- scaleY = gHeight / valueAxisWidth
345
-
346
- return {
347
- translate: [translateX, translateY],
348
- scale: [scaleX, scaleY],
349
- rotate: 0,
350
- rotateX: 0,
351
- rotateY: 0,
352
- value: `translate(${translateX}px, ${translateY}px) scale(${scaleX}, ${scaleY})`
353
- }
354
- }
355
-
356
- return new Observable(subscriber => {
357
- combineLatest({
358
- computedData: computedData$,
359
- fullDataFormatter: fullDataFormatter$,
360
- layout: layout$
361
- }).pipe(
362
- takeUntil(destroy$),
363
- switchMap(async (d) => d),
364
- ).subscribe(data => {
365
- const dataAreaTransformData = calcGridDataAreaTransform ({
366
- data: data.computedData,
367
- groupAxis: data.fullDataFormatter.grid.groupAxis,
368
- valueAxis: data.fullDataFormatter.grid.valueAxis,
369
- width: data.layout.width,
370
- height: data.layout.height
371
- })
372
-
373
- subscriber.next(dataAreaTransformData)
374
- })
375
-
376
- return function unscbscribe () {
377
- destroy$.next(undefined)
378
- }
379
- })
380
- }
381
-
382
- export const gridGraphicReverseScaleObservable = ({ gridContainerPosition$, gridAxesTransform$, gridGraphicTransform$ }: {
383
- gridContainerPosition$: Observable<GridContainerPosition[]>
384
- gridAxesTransform$: Observable<TransformData>
385
- gridGraphicTransform$: Observable<TransformData>
386
- }): Observable<[number, number][]> => {
387
- return combineLatest({
388
- gridContainerPosition: gridContainerPosition$,
389
- gridAxesTransform: gridAxesTransform$,
390
- gridGraphicTransform: gridGraphicTransform$,
391
- }).pipe(
392
- switchMap(async (d) => d),
393
- map(data => {
394
- if (data.gridAxesTransform.rotate == 0 || data.gridAxesTransform.rotate == 180) {
395
- return data.gridContainerPosition.map((series, seriesIndex) => {
396
- return [
397
- 1 / data.gridGraphicTransform.scale[0] / data.gridContainerPosition[seriesIndex].scale[0],
398
- 1 / data.gridGraphicTransform.scale[1] / data.gridContainerPosition[seriesIndex].scale[1],
399
- ]
400
- })
401
- } else {
402
- return data.gridContainerPosition.map((series, seriesIndex) => {
403
- // 由於有垂直的旋轉,所以外層 (container) x和y的scale要互換
404
- return [
405
- 1 / data.gridGraphicTransform.scale[0] / data.gridContainerPosition[seriesIndex].scale[1],
406
- 1 / data.gridGraphicTransform.scale[1] / data.gridContainerPosition[seriesIndex].scale[0],
407
- ]
408
- })
409
- }
410
- }),
411
- )
412
- }
413
-
414
- export const gridAxesSizeObservable = ({ fullDataFormatter$, layout$ }: {
415
- fullDataFormatter$: Observable<DataFormatterGrid>
416
- layout$: Observable<Layout>
417
- }): Observable<{
418
- width: number;
419
- height: number;
420
- }> => {
421
- const destroy$ = new Subject()
422
-
423
- function calcAxesSize ({ xAxisPosition, yAxisPosition, width, height }: {
424
- xAxisPosition: AxisPosition
425
- yAxisPosition: AxisPosition
426
- width: number
427
- height: number
428
- }) {
429
- if ((xAxisPosition === 'bottom' || xAxisPosition === 'top') && (yAxisPosition === 'left' || yAxisPosition === 'right')) {
430
- return { width, height }
431
- } else if ((xAxisPosition === 'left' || xAxisPosition === 'right') && (yAxisPosition === 'bottom' || yAxisPosition === 'top')) {
432
- return {
433
- width: height,
434
- height: width
435
- }
436
- } else {
437
- // default
438
- return { width, height }
439
- }
440
- }
441
-
442
- return new Observable(subscriber => {
443
- combineLatest({
444
- fullDataFormatter: fullDataFormatter$,
445
- layout: layout$
446
- }).pipe(
447
- takeUntil(destroy$),
448
- switchMap(async (d) => d),
449
- ).subscribe(data => {
450
-
451
- const axisSize = calcAxesSize({
452
- xAxisPosition: data.fullDataFormatter.grid.groupAxis.position,
453
- yAxisPosition: data.fullDataFormatter.grid.valueAxis.position,
454
- width: data.layout.width,
455
- height: data.layout.height,
456
- })
457
-
458
- subscriber.next(axisSize)
459
-
460
- return function unsubscribe () {
461
- destroy$.next(undefined)
462
- }
463
- })
464
- })
465
- }
466
-
467
- // export const gridHighlightObservable = ({ computedData$, fullChartParams$, event$ }: {
468
- // computedData$: Observable<ComputedDataTypeMap<'grid'>>
469
- // fullChartParams$: Observable<ChartParams>
470
- // event$: Subject<any>
471
- // }): Observable<string[]> => {
472
- // const datumList$ = computedData$.pipe(
473
- // map(d => d.flat())
474
- // )
475
- // return highlightObservable ({ datumList$, fullChartParams$, event$ })
476
- // }
477
-
478
- export const seriesLabelsObservable = ({ computedData$ }: { computedData$: Observable<ComputedDataTypeMap<'grid'>> }) => {
479
- return computedData$.pipe(
480
- map(data => {
481
- return data
482
- .filter(series => series.length)
483
- .map(series => {
484
- return series[0].seriesLabel
485
- })
486
- }),
487
- distinctUntilChanged((a, b) => {
488
- return JSON.stringify(a).length === JSON.stringify(b).length
489
- }),
490
- )
491
- }
492
-
493
- export const gridVisibleComputedDataObservable = ({ computedData$ }: { computedData$: Observable<ComputedDataTypeMap<'grid'>> }) => {
494
- return computedData$.pipe(
495
- map(data => {
496
- const visibleComputedData = data
497
- .map(d => {
498
- return d.filter(_d => {
499
- return _d.visible == true
500
- })
501
- })
502
- .filter(d => d.length)
503
- return visibleComputedData
504
- })
505
- )
506
- }
507
-
508
- export const gridVisibleComputedLayoutDataObservable = ({ computedLayoutData$ }: { computedLayoutData$: Observable<ComputedLayoutDataGrid> }) => {
509
- return computedLayoutData$.pipe(
510
- map(data => {
511
- const visibleComputedData = data
512
- .map(d => {
513
- return d.filter(_d => {
514
- return _d.visible == true
515
- })
516
- })
517
- .filter(d => d.length)
518
- return visibleComputedData
519
- })
520
- )
521
- }
522
-
523
- // 所有container位置(對應series)
524
- export const gridContainerPositionObservable = ({ computedData$, fullDataFormatter$, layout$ }: {
525
- computedData$: Observable<ComputedDataTypeMap<'grid'>>
526
- fullDataFormatter$: Observable<DataFormatterTypeMap<'grid'>>
527
- layout$: Observable<Layout>
528
- }): Observable<GridContainerPosition[]> => {
529
-
530
- const gridContainerPosition$ = combineLatest({
531
- computedData: computedData$,
532
- fullDataFormatter: fullDataFormatter$,
533
- layout: layout$,
534
- }).pipe(
535
- switchMap(async (d) => d),
536
- map(data => {
537
-
538
- if (data.fullDataFormatter.grid.separateSeries) {
539
- // -- 依slotIndexes計算 --
540
- return calcGridContainerLayout(data.layout, data.fullDataFormatter.container, data.computedData.length)
541
- // return data.computedData.map((seriesData, seriesIndex) => {
542
- // const columnIndex = seriesIndex % data.fullDataFormatter.container.columnAmount
543
- // const rowIndex = Math.floor(seriesIndex / data.fullDataFormatter.container.columnAmount)
544
- // const { translate, scale } = calcGridContainerPosition(data.layout, data.fullDataFormatter.container, rowIndex, columnIndex)
545
- // return {
546
- // slotIndex: seriesIndex,
547
- // rowIndex,
548
- // columnIndex,
549
- // translate,
550
- // scale,
551
- // }
552
- // })
553
- } else {
554
- // -- 無拆分 --
555
- const gridContainerPositionArr = calcGridContainerLayout(data.layout, data.fullDataFormatter.container, 1)
556
- return data.computedData.map((d, i) => gridContainerPositionArr[0]) // 每個series相同位置
557
- // const columnIndex = 0
558
- // const rowIndex = 0
559
- // return data.computedData.map((seriesData, seriesIndex) => {
560
- // const { translate, scale } = calcGridContainerPosition(data.layout, data.fullDataFormatter.container, rowIndex, columnIndex)
561
- // return {
562
- // slotIndex: 0,
563
- // rowIndex,
564
- // columnIndex,
565
- // translate,
566
- // scale,
567
- // }
568
- // })
569
- }
570
- })
571
- )
572
-
573
- return gridContainerPosition$
574
- }
575
-
576
- // 將原本的value全部替換成加總後的value
577
- export const computedStackedDataObservables = ({ isSeriesSeprate$, computedData$ }: {
578
- isSeriesSeprate$: Observable<boolean>
579
- computedData$: Observable<ComputedDataGrid>
580
- }): Observable<ComputedDataGrid> => {
581
- const stackedData$: Observable<ComputedDataGrid> = computedData$.pipe(
582
- map(data => {
583
- // 將同一group的value加總起來
584
- const stackedValue = new Array(data[0] ? data[0].length : 0)
585
- .fill(null)
586
- .map((_, i) => {
587
- return data.reduce((prev, current) => {
588
- if (current && current[i]) {
589
- const currentValue = current[i].value == null || current[i].visible == false
590
- ? 0
591
- : current[i].value!
592
- return prev + currentValue
593
- }
594
- return prev
595
- }, 0)
596
- })
597
- // 將原本的value全部替換成加總後的value
598
- const computedData = data.map((series, seriesIndex) => {
599
- return series.map((d, i) => {
600
- return {
601
- ...d,
602
- value: stackedValue[i],
603
- }
604
- })
605
- })
606
- return computedData
607
- }),
608
- )
609
-
610
- return isSeriesSeprate$.pipe(
611
- switchMap(isSeriesSeprate => {
612
- return iif(() => isSeriesSeprate, computedData$, stackedData$)
613
- })
614
- )
1
+ import {
2
+ combineLatest,
3
+ distinctUntilChanged,
4
+ iif,
5
+ filter,
6
+ map,
7
+ merge,
8
+ takeUntil,
9
+ shareReplay,
10
+ switchMap,
11
+ Subject,
12
+ Observable } from 'rxjs'
13
+ import type {
14
+ AxisPosition,
15
+ ChartType,
16
+ ChartParams,
17
+ ComputedDataTypeMap,
18
+ ComputedDatumTypeMap,
19
+ ComputedDataGrid,
20
+ DataTypeMap,
21
+ DataGridDatum,
22
+ ComputedDatumGrid,
23
+ DataFormatterTypeMap,
24
+ DataFormatterGrid,
25
+ DataFormatterValueAxis,
26
+ DataFormatterGroupAxis,
27
+ ComputedLayoutDatumGrid,
28
+ ComputedLayoutDataGrid,
29
+ GridContainerPosition,
30
+ HighlightTarget,
31
+ Layout,
32
+ TransformData } from '../../lib/core-types'
33
+ import { getMinAndMaxGrid } from './orbchartsUtils'
34
+ import { createAxisLinearScale, createAxisPointScale, createAxisQuantizeScale } from './d3Utils'
35
+ import { calcGridContainerLayout } from './orbchartsUtils'
36
+ import { getMinAndMaxValue } from './orbchartsUtils'
37
+
38
+ export const gridComputedLayoutDataObservable = ({ computedData$, fullDataFormatter$, layout$ }: {
39
+ computedData$: Observable<ComputedDataTypeMap<'grid'>>
40
+ fullDataFormatter$: Observable<DataFormatterTypeMap<'grid'>>
41
+ layout$: Observable<Layout>
42
+ }): Observable<ComputedLayoutDatumGrid[][]> => {
43
+
44
+ // 未篩選group範圍前的group scale
45
+ function createOriginGroupScale (computedData: ComputedDatumGrid[][], dataFormatter: DataFormatterGrid, layout: Layout) {
46
+ const groupAxisWidth = (dataFormatter.grid.groupAxis.position === 'top' || dataFormatter.grid.groupAxis.position === 'bottom')
47
+ ? layout.width
48
+ : layout.height
49
+ const groupEndIndex = computedData[0] ? computedData[0].length - 1 : 0
50
+ const groupScale: d3.ScaleLinear<number, number> = createAxisLinearScale({
51
+ maxValue: groupEndIndex,
52
+ minValue: 0,
53
+ axisWidth: groupAxisWidth,
54
+ scaleDomain: [0, groupEndIndex], // 不使用dataFormatter設定
55
+ scaleRange: [0, 1] // 不使用dataFormatter設定
56
+ })
57
+ return groupScale
58
+ }
59
+
60
+ // 未篩選group範圍及visible前的value scale
61
+ function createOriginValueScale (computedData: ComputedDatumGrid[][], dataFormatter: DataFormatterGrid, layout: Layout) {
62
+ const valueAxisWidth = (dataFormatter.grid.valueAxis.position === 'left' || dataFormatter.grid.valueAxis.position === 'right')
63
+ ? layout.height
64
+ : layout.width
65
+
66
+ const listData = computedData.flat()
67
+ const [minValue, maxValue] = getMinAndMaxValue(listData)
68
+
69
+ const valueScale: d3.ScaleLinear<number, number> = createAxisLinearScale({
70
+ maxValue,
71
+ minValue,
72
+ axisWidth: valueAxisWidth,
73
+ scaleDomain: [minValue, maxValue], // 不使用dataFormatter設定
74
+ scaleRange: [0, 1] // 不使用dataFormatter設定
75
+ })
76
+
77
+ return valueScale
78
+ }
79
+
80
+ return combineLatest({
81
+ computedData: computedData$,
82
+ fullDataFormatter: fullDataFormatter$,
83
+ layout: layout$
84
+ }).pipe(
85
+ switchMap(async d => d),
86
+ map(data => {
87
+ const groupScale = createOriginGroupScale(data.computedData, data.fullDataFormatter, data.layout)
88
+ const valueScale = createOriginValueScale(data.computedData, data.fullDataFormatter, data.layout)
89
+ const zeroY = valueScale(0)
90
+
91
+ return data.computedData.map((seriesData, seriesIndex) => {
92
+ return seriesData.map((groupDatum, groupIndex) => {
93
+ const axisX = groupScale(groupIndex)
94
+ const axisY = valueScale(groupDatum.value ?? 0)
95
+ return {
96
+ ...groupDatum,
97
+ axisX,
98
+ axisY,
99
+ axisYFromZero: axisY - zeroY
100
+ }
101
+ })
102
+ })
103
+ })
104
+ )
105
+ }
106
+
107
+ export const gridAxesTransformObservable = ({ fullDataFormatter$, layout$ }: {
108
+ fullDataFormatter$: Observable<DataFormatterTypeMap<'grid'>>
109
+ layout$: Observable<Layout>
110
+ }): Observable<TransformData> => {
111
+ const destroy$ = new Subject()
112
+
113
+ function calcAxesTransform ({ xAxis, yAxis, width, height }: {
114
+ xAxis: DataFormatterGroupAxis | DataFormatterValueAxis,
115
+ yAxis: DataFormatterValueAxis,
116
+ width: number,
117
+ height: number
118
+ }): TransformData {
119
+ if (!xAxis || !yAxis) {
120
+ return {
121
+ translate: [0, 0],
122
+ scale: [1, 1],
123
+ rotate: 0,
124
+ rotateX: 0,
125
+ rotateY: 0,
126
+ value: ''
127
+ }
128
+ }
129
+ // const width = size.width - fullChartParams.layout.left - fullChartParams.layout.right
130
+ // const height = size.height - fullChartParams.layout.top - fullChartParams.layout.bottom
131
+ let translateX = 0
132
+ let translateY = 0
133
+ let rotate = 0
134
+ let rotateX = 0
135
+ let rotateY = 0
136
+ if (xAxis.position === 'bottom') {
137
+ if (yAxis.position === 'left') {
138
+ rotateX = 180
139
+ translateY = height
140
+ } else if (yAxis.position === 'right') {
141
+ rotateX = 180
142
+ rotateY = 180
143
+ translateX = width
144
+ translateY = height
145
+ } else {
146
+ // 預設
147
+ rotateX = 180
148
+ translateY = height
149
+ }
150
+ } else if (xAxis.position === 'top') {
151
+ if (yAxis.position === 'left') {
152
+ } else if (yAxis.position === 'right') {
153
+ rotateY = 180
154
+ translateX = width
155
+ } else {
156
+ // 預設
157
+ rotateX = 180
158
+ translateY = height
159
+ }
160
+ } else if (xAxis.position === 'left') {
161
+ if (yAxis.position === 'bottom') {
162
+ rotate = -90
163
+ translateY = height
164
+ } else if (yAxis.position === 'top') {
165
+ rotate = -90
166
+ rotateY = 180
167
+ } else {
168
+ // 預設
169
+ rotateX = 180
170
+ translateY = height
171
+ }
172
+ } else if (xAxis.position === 'right') {
173
+ if (yAxis.position === 'bottom') {
174
+ rotate = -90
175
+ rotateX = 180
176
+ translateY = height
177
+ translateX = width
178
+ } else if (yAxis.position === 'top') {
179
+ rotate = -90
180
+ rotateX = 180
181
+ rotateY = 180
182
+ translateX = width
183
+ } else {
184
+ // 預設
185
+ rotateX = 180
186
+ translateY = height
187
+ }
188
+ } else {
189
+ // 預設
190
+ rotateX = 180
191
+ translateY = height
192
+ }
193
+ // selection.style('transform', `translate(${translateX}px, ${translateY}px) rotate(${rotate}deg) rotateX(${rotateX}deg) rotateY(${rotateY}deg)`)
194
+
195
+ return {
196
+ translate: [translateX, translateY],
197
+ scale: [1, 1],
198
+ rotate,
199
+ rotateX,
200
+ rotateY,
201
+ value: `translate(${translateX}px, ${translateY}px) rotate(${rotate}deg) rotateX(${rotateX}deg) rotateY(${rotateY}deg)`
202
+ }
203
+ }
204
+
205
+ return new Observable(subscriber => {
206
+ combineLatest({
207
+ fullDataFormatter: fullDataFormatter$,
208
+ layout: layout$
209
+ }).pipe(
210
+ takeUntil(destroy$),
211
+ switchMap(async (d) => d),
212
+ ).subscribe(data => {
213
+ const axesTransformData = calcAxesTransform({
214
+ xAxis: data.fullDataFormatter.grid.groupAxis,
215
+ yAxis: data.fullDataFormatter.grid.valueAxis,
216
+ width: data.layout.width,
217
+ height: data.layout.height
218
+ })
219
+
220
+ subscriber.next(axesTransformData)
221
+ })
222
+
223
+ return function unscbscribe () {
224
+ destroy$.next(undefined)
225
+ }
226
+ })
227
+ }
228
+
229
+
230
+ export const gridAxesReverseTransformObservable = ({ gridAxesTransform$ }: {
231
+ gridAxesTransform$: Observable<TransformData>
232
+ }): Observable<TransformData> => {
233
+ return gridAxesTransform$.pipe(
234
+ map(d => {
235
+ // const translate: [number, number] = [d.translate[0] * -1, d.translate[1] * -1]
236
+ const translate: [number, number] = [0, 0] // 無需逆轉
237
+ const scale: [number, number] = [1 / d.scale[0], 1 / d.scale[1]]
238
+ const rotate = d.rotate * -1
239
+ const rotateX = d.rotateX * -1
240
+ const rotateY = d.rotateY * -1
241
+ return {
242
+ translate,
243
+ scale,
244
+ rotate,
245
+ rotateX,
246
+ rotateY,
247
+ value: `translate(${translate[0]}px, ${translate[1]}px) rotateX(${rotateX}deg) rotateY(${rotateY}deg) rotate(${rotate}deg)`
248
+ }
249
+ }),
250
+ )
251
+ }
252
+
253
+ export const gridGraphicTransformObservable = ({ computedData$, fullDataFormatter$, layout$ }: {
254
+ computedData$: Observable<ComputedDataTypeMap<'grid'>>
255
+ fullDataFormatter$: Observable<DataFormatterTypeMap<'grid'>>
256
+ layout$: Observable<Layout>
257
+ }): Observable<TransformData> => {
258
+ const destroy$ = new Subject()
259
+
260
+ function calcGridDataAreaTransform ({ data, groupAxis, valueAxis, width, height }: {
261
+ data: ComputedDataTypeMap<'grid'>
262
+ groupAxis: DataFormatterGroupAxis
263
+ valueAxis: DataFormatterValueAxis
264
+ width: number
265
+ height: number
266
+ }): TransformData {
267
+ let translateX = 0
268
+ let translateY = 0
269
+ let scaleX = 0
270
+ let scaleY = 0
271
+
272
+ // -- groupScale --
273
+ const groupAxisWidth = (groupAxis.position === 'top' || groupAxis.position === 'bottom')
274
+ ? width
275
+ : height
276
+ const groupMin = 0
277
+ const groupMax = data[0] ? data[0].length - 1 : 0
278
+ // const groupScaleDomainMin = groupAxis.scaleDomain[0] === 'min'
279
+ // ? groupMin - groupAxis.scalePadding
280
+ // : groupAxis.scaleDomain[0] as number - groupAxis.scalePadding
281
+ const groupScaleDomainMin = groupAxis.scaleDomain[0] - groupAxis.scalePadding
282
+ const groupScaleDomainMax = groupAxis.scaleDomain[1] === 'max'
283
+ ? groupMax + groupAxis.scalePadding
284
+ : groupAxis.scaleDomain[1] as number + groupAxis.scalePadding
285
+
286
+ const groupScale: d3.ScaleLinear<number, number> = createAxisLinearScale({
287
+ maxValue: groupMax,
288
+ minValue: groupMin,
289
+ axisWidth: groupAxisWidth,
290
+ // scaleDomain: groupAxis.scaleDomain,
291
+ scaleDomain: [groupScaleDomainMin, groupScaleDomainMax],
292
+ scaleRange: [0, 1]
293
+ })
294
+
295
+ // -- translateX, scaleX --
296
+ const rangeMinX = groupScale(groupMin)
297
+ const rangeMaxX = groupScale(groupMax)
298
+ if (groupMin == groupMax) {
299
+ // 當group只有一個
300
+ translateX = 0
301
+ scaleX = 1
302
+ } else {
303
+ translateX = rangeMinX
304
+ const gWidth = rangeMaxX - rangeMinX
305
+ scaleX = gWidth / groupAxisWidth
306
+ }
307
+
308
+ // -- valueScale --
309
+ const filteredData = data.map((d, i) => {
310
+ return d.filter((_d, _i) => {
311
+ return _i >= groupScaleDomainMin && _i <= groupScaleDomainMax && _d.visible == true
312
+ })
313
+ })
314
+
315
+ const filteredMinAndMax = getMinAndMaxGrid(filteredData)
316
+ if (filteredMinAndMax[0] === filteredMinAndMax[1]) {
317
+ filteredMinAndMax[0] = filteredMinAndMax[1] - 1 // 避免最大及最小值相同造成無法計算scale
318
+ }
319
+
320
+ const valueAxisWidth = (valueAxis.position === 'left' || valueAxis.position === 'right')
321
+ ? height
322
+ : width
323
+
324
+ const valueScale: d3.ScaleLinear<number, number> = createAxisLinearScale({
325
+ maxValue: filteredMinAndMax[1],
326
+ minValue: filteredMinAndMax[0],
327
+ axisWidth: valueAxisWidth,
328
+ scaleDomain: valueAxis.scaleDomain,
329
+ scaleRange: valueAxis.scaleRange
330
+ })
331
+
332
+ // -- translateY, scaleY --
333
+ const minAndMax = getMinAndMaxGrid(data)
334
+ if (minAndMax[0] === minAndMax[1]) {
335
+ minAndMax[0] = minAndMax[1] - 1 // 避免最大及最小值相同造成無法計算scale
336
+ }
337
+ const rangeMinY = valueScale(minAndMax[0])
338
+ const rangeMaxY = valueScale(minAndMax[1])
339
+ translateY = rangeMinY
340
+ const gHeight = rangeMaxY - rangeMinY
341
+ scaleY = gHeight / valueAxisWidth
342
+
343
+ return {
344
+ translate: [translateX, translateY],
345
+ scale: [scaleX, scaleY],
346
+ rotate: 0,
347
+ rotateX: 0,
348
+ rotateY: 0,
349
+ value: `translate(${translateX}px, ${translateY}px) scale(${scaleX}, ${scaleY})`
350
+ }
351
+ }
352
+
353
+ return new Observable(subscriber => {
354
+ combineLatest({
355
+ computedData: computedData$,
356
+ fullDataFormatter: fullDataFormatter$,
357
+ layout: layout$
358
+ }).pipe(
359
+ takeUntil(destroy$),
360
+ switchMap(async (d) => d),
361
+ ).subscribe(data => {
362
+ const dataAreaTransformData = calcGridDataAreaTransform ({
363
+ data: data.computedData,
364
+ groupAxis: data.fullDataFormatter.grid.groupAxis,
365
+ valueAxis: data.fullDataFormatter.grid.valueAxis,
366
+ width: data.layout.width,
367
+ height: data.layout.height
368
+ })
369
+
370
+ subscriber.next(dataAreaTransformData)
371
+ })
372
+
373
+ return function unscbscribe () {
374
+ destroy$.next(undefined)
375
+ }
376
+ })
377
+ }
378
+
379
+ export const gridGraphicReverseScaleObservable = ({ gridContainerPosition$, gridAxesTransform$, gridGraphicTransform$ }: {
380
+ gridContainerPosition$: Observable<GridContainerPosition[]>
381
+ gridAxesTransform$: Observable<TransformData>
382
+ gridGraphicTransform$: Observable<TransformData>
383
+ }): Observable<[number, number][]> => {
384
+ return combineLatest({
385
+ gridContainerPosition: gridContainerPosition$,
386
+ gridAxesTransform: gridAxesTransform$,
387
+ gridGraphicTransform: gridGraphicTransform$,
388
+ }).pipe(
389
+ switchMap(async (d) => d),
390
+ map(data => {
391
+ if (data.gridAxesTransform.rotate == 0 || data.gridAxesTransform.rotate == 180) {
392
+ return data.gridContainerPosition.map((series, seriesIndex) => {
393
+ return [
394
+ 1 / data.gridGraphicTransform.scale[0] / data.gridContainerPosition[seriesIndex].scale[0],
395
+ 1 / data.gridGraphicTransform.scale[1] / data.gridContainerPosition[seriesIndex].scale[1],
396
+ ]
397
+ })
398
+ } else {
399
+ return data.gridContainerPosition.map((series, seriesIndex) => {
400
+ // 由於有垂直的旋轉,所以外層 (container) x和y的scale要互換
401
+ return [
402
+ 1 / data.gridGraphicTransform.scale[0] / data.gridContainerPosition[seriesIndex].scale[1],
403
+ 1 / data.gridGraphicTransform.scale[1] / data.gridContainerPosition[seriesIndex].scale[0],
404
+ ]
405
+ })
406
+ }
407
+ }),
408
+ )
409
+ }
410
+
411
+ export const gridAxesSizeObservable = ({ fullDataFormatter$, layout$ }: {
412
+ fullDataFormatter$: Observable<DataFormatterGrid>
413
+ layout$: Observable<Layout>
414
+ }): Observable<{
415
+ width: number;
416
+ height: number;
417
+ }> => {
418
+ const destroy$ = new Subject()
419
+
420
+ function calcAxesSize ({ xAxisPosition, yAxisPosition, width, height }: {
421
+ xAxisPosition: AxisPosition
422
+ yAxisPosition: AxisPosition
423
+ width: number
424
+ height: number
425
+ }) {
426
+ if ((xAxisPosition === 'bottom' || xAxisPosition === 'top') && (yAxisPosition === 'left' || yAxisPosition === 'right')) {
427
+ return { width, height }
428
+ } else if ((xAxisPosition === 'left' || xAxisPosition === 'right') && (yAxisPosition === 'bottom' || yAxisPosition === 'top')) {
429
+ return {
430
+ width: height,
431
+ height: width
432
+ }
433
+ } else {
434
+ // default
435
+ return { width, height }
436
+ }
437
+ }
438
+
439
+ return new Observable(subscriber => {
440
+ combineLatest({
441
+ fullDataFormatter: fullDataFormatter$,
442
+ layout: layout$
443
+ }).pipe(
444
+ takeUntil(destroy$),
445
+ switchMap(async (d) => d),
446
+ ).subscribe(data => {
447
+
448
+ const axisSize = calcAxesSize({
449
+ xAxisPosition: data.fullDataFormatter.grid.groupAxis.position,
450
+ yAxisPosition: data.fullDataFormatter.grid.valueAxis.position,
451
+ width: data.layout.width,
452
+ height: data.layout.height,
453
+ })
454
+
455
+ subscriber.next(axisSize)
456
+
457
+ return function unsubscribe () {
458
+ destroy$.next(undefined)
459
+ }
460
+ })
461
+ })
462
+ }
463
+
464
+ // export const gridHighlightObservable = ({ computedData$, fullChartParams$, event$ }: {
465
+ // computedData$: Observable<ComputedDataTypeMap<'grid'>>
466
+ // fullChartParams$: Observable<ChartParams>
467
+ // event$: Subject<any>
468
+ // }): Observable<string[]> => {
469
+ // const datumList$ = computedData$.pipe(
470
+ // map(d => d.flat())
471
+ // )
472
+ // return highlightObservable ({ datumList$, fullChartParams$, event$ })
473
+ // }
474
+
475
+ export const gridSeriesLabelsObservable = ({ computedData$ }: { computedData$: Observable<ComputedDataTypeMap<'grid'>> }) => {
476
+ return computedData$.pipe(
477
+ map(data => {
478
+ return data
479
+ .filter(series => series.length)
480
+ .map(series => {
481
+ return series[0].seriesLabel
482
+ })
483
+ }),
484
+ distinctUntilChanged((a, b) => {
485
+ return JSON.stringify(a).length === JSON.stringify(b).length
486
+ }),
487
+ )
488
+ }
489
+
490
+ export const gridVisibleComputedDataObservable = ({ computedData$ }: { computedData$: Observable<ComputedDataTypeMap<'grid'>> }) => {
491
+ return computedData$.pipe(
492
+ map(data => {
493
+ const visibleComputedData = data
494
+ .map(d => {
495
+ return d.filter(_d => {
496
+ return _d.visible == true
497
+ })
498
+ })
499
+ .filter(d => d.length)
500
+ return visibleComputedData
501
+ })
502
+ )
503
+ }
504
+
505
+ export const gridVisibleComputedLayoutDataObservable = ({ computedLayoutData$ }: { computedLayoutData$: Observable<ComputedLayoutDataGrid> }) => {
506
+ return computedLayoutData$.pipe(
507
+ map(data => {
508
+ const visibleComputedData = data
509
+ .map(d => {
510
+ return d.filter(_d => {
511
+ return _d.visible == true
512
+ })
513
+ })
514
+ .filter(d => d.length)
515
+ return visibleComputedData
516
+ })
517
+ )
518
+ }
519
+
520
+ // 所有container位置(對應series)
521
+ export const gridContainerPositionObservable = ({ computedData$, fullDataFormatter$, layout$ }: {
522
+ computedData$: Observable<ComputedDataTypeMap<'grid'>>
523
+ fullDataFormatter$: Observable<DataFormatterTypeMap<'grid'>>
524
+ layout$: Observable<Layout>
525
+ }): Observable<GridContainerPosition[]> => {
526
+
527
+ const gridContainerPosition$ = combineLatest({
528
+ computedData: computedData$,
529
+ fullDataFormatter: fullDataFormatter$,
530
+ layout: layout$,
531
+ }).pipe(
532
+ switchMap(async (d) => d),
533
+ map(data => {
534
+
535
+ if (data.fullDataFormatter.grid.separateSeries) {
536
+ // -- 依slotIndexes計算 --
537
+ return calcGridContainerLayout(data.layout, data.fullDataFormatter.container, data.computedData.length)
538
+ // return data.computedData.map((seriesData, seriesIndex) => {
539
+ // const columnIndex = seriesIndex % data.fullDataFormatter.container.columnAmount
540
+ // const rowIndex = Math.floor(seriesIndex / data.fullDataFormatter.container.columnAmount)
541
+ // const { translate, scale } = calcGridContainerPosition(data.layout, data.fullDataFormatter.container, rowIndex, columnIndex)
542
+ // return {
543
+ // slotIndex: seriesIndex,
544
+ // rowIndex,
545
+ // columnIndex,
546
+ // translate,
547
+ // scale,
548
+ // }
549
+ // })
550
+ } else {
551
+ // -- 無拆分 --
552
+ const gridContainerPositionArr = calcGridContainerLayout(data.layout, data.fullDataFormatter.container, 1)
553
+ return data.computedData.map((d, i) => gridContainerPositionArr[0]) // 每個series相同位置
554
+ // const columnIndex = 0
555
+ // const rowIndex = 0
556
+ // return data.computedData.map((seriesData, seriesIndex) => {
557
+ // const { translate, scale } = calcGridContainerPosition(data.layout, data.fullDataFormatter.container, rowIndex, columnIndex)
558
+ // return {
559
+ // slotIndex: 0,
560
+ // rowIndex,
561
+ // columnIndex,
562
+ // translate,
563
+ // scale,
564
+ // }
565
+ // })
566
+ }
567
+ })
568
+ )
569
+
570
+ return gridContainerPosition$
571
+ }
572
+
573
+ // 將原本的value全部替換成加總後的value
574
+ export const computedStackedDataObservables = ({ isSeriesSeprate$, computedData$ }: {
575
+ isSeriesSeprate$: Observable<boolean>
576
+ computedData$: Observable<ComputedDataGrid>
577
+ }): Observable<ComputedDataGrid> => {
578
+ const stackedData$: Observable<ComputedDataGrid> = computedData$.pipe(
579
+ map(data => {
580
+ // 將同一group的value加總起來
581
+ const stackedValue = new Array(data[0] ? data[0].length : 0)
582
+ .fill(null)
583
+ .map((_, i) => {
584
+ return data.reduce((prev, current) => {
585
+ if (current && current[i]) {
586
+ const currentValue = current[i].value == null || current[i].visible == false
587
+ ? 0
588
+ : current[i].value!
589
+ return prev + currentValue
590
+ }
591
+ return prev
592
+ }, 0)
593
+ })
594
+ // 將原本的value全部替換成加總後的value
595
+ const computedData = data.map((series, seriesIndex) => {
596
+ return series.map((d, i) => {
597
+ return {
598
+ ...d,
599
+ value: stackedValue[i],
600
+ }
601
+ })
602
+ })
603
+ return computedData
604
+ }),
605
+ )
606
+
607
+ return isSeriesSeprate$.pipe(
608
+ switchMap(isSeriesSeprate => {
609
+ return iif(() => isSeriesSeprate, computedData$, stackedData$)
610
+ })
611
+ )
615
612
  }