@orbcharts/core 3.0.0-alpha.55 → 3.0.0-alpha.57

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