@orbcharts/core 3.0.0-alpha.56 → 3.0.0-alpha.58

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 +221 -217
  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) 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] === '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
  }