@orbcharts/core 3.0.0-alpha.48 → 3.0.0-alpha.49

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