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

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 (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
  }