@orbcharts/core 3.0.0-beta.9 → 3.0.1

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 (35) hide show
  1. package/dist/orbcharts-core.es.js +2784 -2403
  2. package/dist/orbcharts-core.umd.js +4 -4
  3. package/dist/src/defaults.d.ts +2 -1
  4. package/dist/src/utils/gridObservables.d.ts +8 -4
  5. package/dist/src/utils/index.d.ts +0 -3
  6. package/dist/src/utils/multiGridObservables.d.ts +3 -2
  7. package/dist/src/utils/multiValueObservables.d.ts +76 -29
  8. package/dist/src/utils/observables.d.ts +8 -1
  9. package/dist/src/utils/orbchartsUtils.d.ts +9 -9
  10. package/dist/src/utils/seriesObservables.d.ts +1 -1
  11. package/package.json +2 -2
  12. package/src/base/createBaseChart.ts +4 -3
  13. package/src/base/createBasePlugin.ts +5 -4
  14. package/src/base/validators/chartParamsValidator.ts +4 -4
  15. package/src/defaults.ts +54 -10
  16. package/src/grid/contextObserverCallback.ts +31 -9
  17. package/src/grid/dataFormatterValidator.ts +42 -23
  18. package/src/multiGrid/contextObserverCallback.ts +38 -7
  19. package/src/multiValue/computedDataFn.ts +4 -1
  20. package/src/multiValue/contextObserverCallback.ts +159 -43
  21. package/src/multiValue/dataFormatterValidator.ts +85 -5
  22. package/src/multiValue/dataValidator.ts +9 -6
  23. package/src/relationship/computedDataFn.ts +37 -22
  24. package/src/relationship/dataFormatterValidator.ts +10 -6
  25. package/src/relationship/dataValidator.ts +10 -6
  26. package/src/series/contextObserverCallback.ts +18 -11
  27. package/src/tree/dataValidator.ts +1 -1
  28. package/src/utils/gridObservables.ts +32 -10
  29. package/src/utils/index.ts +3 -3
  30. package/src/utils/multiGridObservables.ts +34 -25
  31. package/src/utils/multiValueObservables.ts +479 -97
  32. package/src/utils/observables.ts +77 -15
  33. package/src/utils/orbchartsUtils.ts +9 -9
  34. package/src/utils/seriesObservables.ts +4 -4
  35. package/src/utils/validator.ts +1 -1
@@ -18,41 +18,50 @@ import type {
18
18
  ComputedDatumTypeMap,
19
19
  ComputedDataMultiValue,
20
20
  ComputedDatumMultiValue,
21
+ ComputedDatumWithSumMultiValue,
22
+ ContainerSize,
21
23
  DataFormatterTypeMap,
22
24
  DataFormatterMultiValue,
23
- DataFormatterAxis,
24
- ComputedLayoutDatumMultiValue,
25
- ComputedLayoutDataMultiValue,
25
+ DataFormatterXYAxis,
26
+ ComputedXYDatumMultiValue,
27
+ ComputedXYDataMultiValue,
26
28
  ContainerPositionScaled,
27
29
  HighlightTarget,
28
30
  Layout,
29
31
  TransformData } from '../../lib/core-types'
30
32
  import { getMinMax, getMinMaxMultiValue } from './orbchartsUtils'
31
33
  import { createValueToAxisScale, createLabelToAxisScale, createAxisToLabelIndexScale } from './d3Scale'
32
- import { calcGridContainerLayout } from './orbchartsUtils'
34
+ import { calcContainerPositionScaled } from './orbchartsUtils'
33
35
 
34
- export const minMaxXYObservable = ({ computedData$ }: { computedData$: Observable<ComputedDataTypeMap<'multiValue'>> }) => {
35
- return computedData$.pipe(
36
+ export const xyMinMaxObservable = ({ computedData$, xyValueIndex$ }: {
37
+ computedData$: Observable<ComputedDataTypeMap<'multiValue'>>
38
+ xyValueIndex$: Observable<[number, number]>
39
+ }) => {
40
+ return combineLatest({
41
+ computedData: computedData$,
42
+ xyValueIndex: xyValueIndex$,
43
+ }).pipe(
36
44
  map(data => {
37
- const flatData = data.flat()
38
- const [minX, maxX] = getMinMax(flatData.map(d => d.value[0]))
39
- const [minY, maxY] = getMinMax(flatData.map(d => d.value[1]))
45
+ const flatData = data.computedData.flat()
46
+ const [minX, maxX] = getMinMax(flatData.map(d => d.value[data.xyValueIndex[0]]))
47
+ const [minY, maxY] = getMinMax(flatData.map(d => d.value[data.xyValueIndex[1]]))
40
48
  return { minX, maxX, minY, maxY }
41
49
  })
42
50
  )
43
51
  }
44
52
 
45
- export const multiValueComputedLayoutDataObservable = ({ computedData$, minMaxXY$, fullDataFormatter$, layout$ }: {
53
+ export const computedXYDataObservable = ({ computedData$, xyMinMax$, xyValueIndex$, fullDataFormatter$, layout$ }: {
46
54
  computedData$: Observable<ComputedDataTypeMap<'multiValue'>>
47
- minMaxXY$: Observable<{ minX: number, maxX: number, minY: number, maxY: number }>
55
+ xyMinMax$: Observable<{ minX: number, maxX: number, minY: number, maxY: number }>
56
+ xyValueIndex$: Observable<[number, number]>
48
57
  fullDataFormatter$: Observable<DataFormatterTypeMap<'multiValue'>>
49
58
  layout$: Observable<Layout>
50
- }): Observable<ComputedLayoutDataMultiValue> => {
59
+ }): Observable<ComputedXYDataMultiValue> => {
51
60
 
52
61
  // 未篩選範圍前的 scale
53
- function createOriginXScale (minMaxXY: { minX: number, maxX: number, minY: number, maxY: number }, layout: Layout) {
54
- let maxValue = minMaxXY.maxX
55
- let minValue = minMaxXY.minX
62
+ function createOriginXScale (xyMinMax: { minX: number, maxX: number, minY: number, maxY: number }, layout: Layout) {
63
+ let maxValue = xyMinMax.maxX
64
+ let minValue = xyMinMax.minX
56
65
  if (minValue === maxValue && maxValue === 0) {
57
66
  // 避免最大及最小值相同造成無法計算scale
58
67
  maxValue = 1
@@ -69,9 +78,9 @@ export const multiValueComputedLayoutDataObservable = ({ computedData$, minMaxXY
69
78
  }
70
79
 
71
80
  // 未篩選範圍及visible前的 scale
72
- function createOriginYScale (minMaxXY: { minX: number, maxX: number, minY: number, maxY: number }, layout: Layout) {
73
- let maxValue = minMaxXY.maxY
74
- let minValue = minMaxXY.minY
81
+ function createOriginYScale (xyMinMax: { minX: number, maxX: number, minY: number, maxY: number }, layout: Layout) {
82
+ let maxValue = xyMinMax.maxY
83
+ let minValue = xyMinMax.minY
75
84
  if (minValue === maxValue && maxValue === 0) {
76
85
  // 避免最大及最小值相同造成無法計算scale
77
86
  maxValue = 1
@@ -90,24 +99,25 @@ export const multiValueComputedLayoutDataObservable = ({ computedData$, minMaxXY
90
99
 
91
100
  return combineLatest({
92
101
  computedData: computedData$,
93
- minMaxXY: minMaxXY$,
102
+ xyMinMax: xyMinMax$,
103
+ xyValueIndex: xyValueIndex$,
94
104
  fullDataFormatter: fullDataFormatter$,
95
105
  layout: layout$
96
106
  }).pipe(
97
107
  switchMap(async d => d),
98
108
  map(data => {
99
109
 
100
- const xScale = createOriginXScale(data.minMaxXY, data.layout)
101
- const yScale = createOriginYScale(data.minMaxXY, data.layout)
110
+ const xScale = createOriginXScale(data.xyMinMax, data.layout)
111
+ const yScale = createOriginYScale(data.xyMinMax, data.layout)
102
112
 
103
113
  return data.computedData
104
114
  .map((categoryData, categoryIndex) => {
105
115
  return categoryData.map((datum, datumIndex) => {
106
116
  return {
107
117
  ...datum,
108
- axisX: xScale(datum.value[0] ?? 0),
118
+ axisX: xScale(datum.value[data.xyValueIndex[0]] ?? 0),
109
119
  // axisY: data.layout.height - yScale(datum.value[1] ?? 0), // y軸的繪圖座標是從上到下,所以反轉
110
- axisY: yScale(datum.value[1] ?? 0), // y軸的繪圖座標是從上到下,所以反轉
120
+ axisY: yScale(datum.value[data.xyValueIndex[1]] ?? 0), // y軸的繪圖座標是從上到下,所以反轉
111
121
  }
112
122
  })
113
123
  })
@@ -122,8 +132,8 @@ export const multiValueComputedLayoutDataObservable = ({ computedData$, minMaxXY
122
132
  // const destroy$ = new Subject()
123
133
 
124
134
  // function calcAxesTransform ({ xAxis, yAxis, width, height }: {
125
- // xAxis: DataFormatterAxis,
126
- // yAxis: DataFormatterAxis,
135
+ // xAxis: DataFormatterXYAxis,
136
+ // yAxis: DataFormatterXYAxis,
127
137
  // width: number,
128
138
  // height: number
129
139
  // }): TransformData {
@@ -258,7 +268,7 @@ export const multiValueComputedLayoutDataObservable = ({ computedData$, minMaxXY
258
268
  // })
259
269
  // }
260
270
 
261
- // export const multiValueHighlightObservable = ({ computedData$, fullChartParams$, event$ }: {
271
+ // export const highlightObservable = ({ computedData$, fullChartParams$, event$ }: {
262
272
  // computedData$: Observable<ComputedDataTypeMap<'multiValue'>>
263
273
  // fullChartParams$: Observable<ChartParams>
264
274
  // event$: Subject<any>
@@ -269,7 +279,7 @@ export const multiValueComputedLayoutDataObservable = ({ computedData$, minMaxXY
269
279
  // return highlightObservable ({ datumList$, fullChartParams$, event$ })
270
280
  // }
271
281
 
272
- export const multiValueCategoryLabelsObservable = ({ computedData$, fullDataFormatter$ }: {
282
+ export const categoryLabelsObservable = ({ computedData$, fullDataFormatter$ }: {
273
283
  computedData$: Observable<ComputedDataTypeMap<'multiValue'>>
274
284
  fullDataFormatter$: Observable<DataFormatterTypeMap<'multiValue'>>
275
285
  }) => {
@@ -285,7 +295,7 @@ export const multiValueCategoryLabelsObservable = ({ computedData$, fullDataForm
285
295
  )
286
296
  }
287
297
 
288
- export const multiValueVisibleComputedDataObservable = ({ computedData$ }: { computedData$: Observable<ComputedDataTypeMap<'multiValue'>> }) => {
298
+ export const visibleComputedDataObservable = ({ computedData$ }: { computedData$: Observable<ComputedDataTypeMap<'multiValue'>> }) => {
289
299
  return computedData$.pipe(
290
300
  map(data => {
291
301
  return data
@@ -299,8 +309,100 @@ export const multiValueVisibleComputedDataObservable = ({ computedData$ }: { com
299
309
  )
300
310
  }
301
311
 
302
- export const multiValueVisibleComputedLayoutDataObservable = ({ computedLayoutData$ }: { computedLayoutData$: Observable<ComputedLayoutDataMultiValue> }) => {
303
- return computedLayoutData$.pipe(
312
+ export const visibleComputedSumDataObservable = ({ visibleComputedData$ }: { visibleComputedData$: Observable<ComputedDataMultiValue> }) => {
313
+ return visibleComputedData$.pipe(
314
+ map(data => {
315
+ return data.map(categoryData => {
316
+ return categoryData
317
+ .map(d => {
318
+ let newDatum = d as ComputedDatumWithSumMultiValue
319
+ // 新增總計資料欄位
320
+ newDatum.sum = newDatum.value.reduce((acc, curr) => acc + curr, 0)
321
+ return newDatum
322
+ })
323
+ })
324
+ })
325
+ )
326
+ }
327
+
328
+ // Ranking資料 - 用 value[index] 排序
329
+ export const visibleComputedRankingByIndexDataObservable = ({ xyValueIndex$, isCategorySeprate$, visibleComputedData$ }: {
330
+ xyValueIndex$: Observable<[number, number]>
331
+ isCategorySeprate$: Observable<boolean>
332
+ visibleComputedData$: Observable<ComputedDatumMultiValue[][]>
333
+ }) => {
334
+
335
+ return combineLatest({
336
+ isCategorySeprate: isCategorySeprate$,
337
+ xyValueIndex: xyValueIndex$,
338
+ visibleComputedData: visibleComputedData$
339
+ }).pipe(
340
+ switchMap(async d => d),
341
+ map(data => {
342
+ const xValueIndex = data.xyValueIndex[0]
343
+ // -- category 分開 --
344
+ if (data.isCategorySeprate) {
345
+ return data.visibleComputedData
346
+ .map(categoryData => {
347
+ return categoryData
348
+ .sort((a, b) => {
349
+ const bValue = b.value[xValueIndex] ?? - Infinity // - Infinity 為最小值
350
+ const aValue = a.value[xValueIndex] ?? - Infinity
351
+
352
+ return bValue - aValue
353
+ })
354
+ })
355
+ // -- 用 value[index] 排序 --
356
+ } else {
357
+ return [
358
+ data.visibleComputedData
359
+ .flat()
360
+ .sort((a, b) => {
361
+ const bValue = b.value[xValueIndex] ?? - Infinity // - Infinity 為最小值
362
+ const aValue = a.value[xValueIndex] ?? - Infinity
363
+
364
+ return bValue - aValue
365
+ })
366
+ ]
367
+ }
368
+ })
369
+ )
370
+ }
371
+
372
+ // Ranking資料 - 用所有 valueIndex 加總資料排序
373
+ export const visibleComputedRankingBySumDataObservable = ({ isCategorySeprate$, visibleComputedSumData$ }: {
374
+ isCategorySeprate$: Observable<boolean>
375
+ // visibleComputedData$: Observable<ComputedDatumMultiValue[][]>
376
+ visibleComputedSumData$: Observable<ComputedDatumWithSumMultiValue[][]>
377
+ }) => {
378
+
379
+ return combineLatest({
380
+ isCategorySeprate: isCategorySeprate$,
381
+ visibleComputedSumData: visibleComputedSumData$
382
+ }).pipe(
383
+ switchMap(async d => d),
384
+ map(data => {
385
+ // -- category 分開 --
386
+ if (data.isCategorySeprate) {
387
+ return data.visibleComputedSumData
388
+ .map(categoryData => {
389
+ return categoryData
390
+ .sort((a, b) => b.sum - a.sum)
391
+ })
392
+ // -- 用 value[index] 排序 --
393
+ } else {
394
+ return [
395
+ data.visibleComputedSumData
396
+ .flat()
397
+ .sort((a, b) => b.sum - a.sum)
398
+ ]
399
+ }
400
+ })
401
+ )
402
+ }
403
+
404
+ export const visibleComputedXYDataObservable = ({ computedXYData$ }: { computedXYData$: Observable<ComputedXYDataMultiValue> }) => {
405
+ return computedXYData$.pipe(
304
406
  map(data => {
305
407
  return data
306
408
  .map(categoryData => {
@@ -313,14 +415,14 @@ export const multiValueVisibleComputedLayoutDataObservable = ({ computedLayoutDa
313
415
  )
314
416
  }
315
417
 
316
- // 所有container位置(對應series
317
- export const multiValueContainerPositionObservable = ({ computedData$, fullDataFormatter$, layout$ }: {
418
+ // 所有container位置(對應category
419
+ export const containerPositionObservable = ({ computedData$, fullDataFormatter$, layout$ }: {
318
420
  computedData$: Observable<ComputedDataTypeMap<'multiValue'>>
319
421
  fullDataFormatter$: Observable<DataFormatterTypeMap<'multiValue'>>
320
422
  layout$: Observable<Layout>
321
423
  }): Observable<ContainerPositionScaled[]> => {
322
424
 
323
- const multiValueContainerPosition$ = combineLatest({
425
+ const containerPosition$ = combineLatest({
324
426
  computedData: computedData$,
325
427
  fullDataFormatter: fullDataFormatter$,
326
428
  layout: layout$,
@@ -330,7 +432,7 @@ export const multiValueContainerPositionObservable = ({ computedData$, fullDataF
330
432
 
331
433
  if (data.fullDataFormatter.separateCategory) {
332
434
  // -- 依slotIndexes計算 --
333
- return calcGridContainerLayout(data.layout, data.fullDataFormatter.container, data.computedData.length)
435
+ return calcContainerPositionScaled(data.layout, data.fullDataFormatter.container, data.computedData.length)
334
436
  // return data.computedData.map((seriesData, seriesIndex) => {
335
437
  // const columnIndex = seriesIndex % data.fullDataFormatter.container.columnAmount
336
438
  // const rowIndex = Math.floor(seriesIndex / data.fullDataFormatter.container.columnAmount)
@@ -345,8 +447,8 @@ export const multiValueContainerPositionObservable = ({ computedData$, fullDataF
345
447
  // })
346
448
  } else {
347
449
  // -- 無拆分 --
348
- const multiValueContainerPositionArr = calcGridContainerLayout(data.layout, data.fullDataFormatter.container, 1)
349
- return data.computedData.map((d, i) => multiValueContainerPositionArr[0]) // 每個series相同位置
450
+ const containerPositionArr = calcContainerPositionScaled(data.layout, data.fullDataFormatter.container, 1)
451
+ return data.computedData.map((d, i) => containerPositionArr[0]) // 每個series相同位置
350
452
  // const columnIndex = 0
351
453
  // const rowIndex = 0
352
454
  // return data.computedData.map((seriesData, seriesIndex) => {
@@ -363,25 +465,69 @@ export const multiValueContainerPositionObservable = ({ computedData$, fullDataF
363
465
  })
364
466
  )
365
467
 
366
- return multiValueContainerPosition$
468
+ return containerPosition$
367
469
  }
368
470
 
471
+ // export const containerSizeObservable = ({ layout$, containerPosition$ }: {
472
+ // layout$: Observable<Layout>
473
+ // containerPosition$: Observable<ContainerPositionScaled[]>
474
+ // }) => {
475
+ // const rowAmount$ = containerPosition$.pipe(
476
+ // map(containerPosition => {
477
+ // const maxRowIndex = containerPosition.reduce((acc, current) => {
478
+ // return current.rowIndex > acc ? current.rowIndex : acc
479
+ // }, 0)
480
+ // return maxRowIndex + 1
481
+ // }),
482
+ // distinctUntilChanged(),
483
+ // )
484
+
485
+ // const columnAmount$ = containerPosition$.pipe(
486
+ // map(containerPosition => {
487
+ // const maxColumnIndex = containerPosition.reduce((acc, current) => {
488
+ // return current.columnIndex > acc ? current.columnIndex : acc
489
+ // }, 0)
490
+ // return maxColumnIndex + 1
491
+ // }),
492
+ // distinctUntilChanged()
493
+ // )
369
494
 
370
- export const filteredMinMaxXYDataObservable = ({ visibleComputedLayoutData$, minMaxXY$, fullDataFormatter$ }: {
371
- visibleComputedLayoutData$: Observable<ComputedLayoutDataMultiValue>
372
- minMaxXY$: Observable<{ minX: number, maxX: number, minY: number, maxY: number }>
495
+ // return combineLatest({
496
+ // layout: layout$,
497
+ // rowAmount: rowAmount$,
498
+ // columnAmount: columnAmount$
499
+ // }).pipe(
500
+ // switchMap(async (d) => d),
501
+ // map(data => {
502
+ // const width = (data.layout.rootWidth / data.columnAmount) - (data.layout.left + data.layout.right)
503
+ // const height = (data.layout.rootHeight / data.rowAmount) - (data.layout.top + data.layout.bottom)
504
+ // return {
505
+ // width,
506
+ // height
507
+ // }
508
+ // }),
509
+ // distinctUntilChanged((a, b) => a.width === b.width && a.height === b.height),
510
+ // shareReplay(1)
511
+ // )
512
+ // }
513
+
514
+ export const filteredXYMinMaxDataObservable = ({ visibleComputedXYData$, xyMinMax$, xyValueIndex$, fullDataFormatter$ }: {
515
+ visibleComputedXYData$: Observable<ComputedXYDataMultiValue>
516
+ xyMinMax$: Observable<{ minX: number, maxX: number, minY: number, maxY: number }>
517
+ xyValueIndex$: Observable<[number, number]>
373
518
  fullDataFormatter$: Observable<DataFormatterTypeMap<'multiValue'>>
374
519
  }) => {
375
520
  return combineLatest({
376
- visibleComputedLayoutData: visibleComputedLayoutData$,
377
- minMaxXY: minMaxXY$,
521
+ visibleComputedXYData: visibleComputedXYData$,
522
+ xyMinMax: xyMinMax$,
523
+ xyValueIndex: xyValueIndex$,
378
524
  fullDataFormatter: fullDataFormatter$,
379
525
  }).pipe(
380
526
  map(data => {
381
527
  // 所有可見資料依 dataFormatter 的 scale 設定篩選出最大小值
382
528
  const { minX, maxX, minY, maxY } = (() => {
383
529
 
384
- let { minX, maxX, minY, maxY } = data.minMaxXY
530
+ let { minX, maxX, minY, maxY } = data.xyMinMax
385
531
 
386
532
  if (data.fullDataFormatter.xAxis.scaleDomain[0] === 'auto' && minX > 0) {
387
533
  minX = 0
@@ -407,29 +553,31 @@ export const filteredMinMaxXYDataObservable = ({ visibleComputedLayoutData$, min
407
553
  return { minX, maxX, minY, maxY }
408
554
  })()
409
555
  // console.log({ minX, maxX, minY, maxY })
410
- let datumList: ComputedLayoutDatumMultiValue[] = []
411
- let minXDatum: ComputedLayoutDatumMultiValue | null = null
412
- let maxXDatum: ComputedLayoutDatumMultiValue | null = null
413
- let minYDatum: ComputedLayoutDatumMultiValue | null = null
414
- let maxYDatum: ComputedLayoutDatumMultiValue | null = null
415
- // console.log('data.visibleComputedLayoutData', data.visibleComputedLayoutData)
556
+ let datumList: ComputedXYDatumMultiValue[] = []
557
+ let minXDatum: ComputedXYDatumMultiValue | null = null
558
+ let maxXDatum: ComputedXYDatumMultiValue | null = null
559
+ let minYDatum: ComputedXYDatumMultiValue | null = null
560
+ let maxYDatum: ComputedXYDatumMultiValue | null = null
561
+ // console.log('data.visibleComputedXYData', data.visibleComputedXYData)
416
562
  // minX, maxX, minY, maxY 範圍內的最大最小值資料
417
563
  // console.log({ minX, maxX, minY, maxY })
418
- for (let categoryData of data.visibleComputedLayoutData) {
564
+ for (let categoryData of data.visibleComputedXYData) {
419
565
  for (let datum of categoryData) {
566
+ const xValue = datum.value[data.xyValueIndex[0]]
567
+ const yValue = datum.value[data.xyValueIndex[1]]
420
568
  // 比較矩形範圍(所以 minX, maxX, minY, maxY 要同時比較)
421
- if (datum.value[0] >= minX && datum.value[0] <= maxX && datum.value[1] >= minY && datum.value[1] <= maxY) {
569
+ if (xValue >= minX && xValue <= maxX && yValue >= minY && yValue <= maxY) {
422
570
  datumList.push(datum)
423
- if (minXDatum == null || datum.value[0] < minXDatum.value[0]) {
571
+ if (minXDatum == null || xValue < minXDatum.value[data.xyValueIndex[0]]) {
424
572
  minXDatum = datum
425
573
  }
426
- if (maxXDatum == null || datum.value[0] > maxXDatum.value[0]) {
574
+ if (maxXDatum == null || xValue > maxXDatum.value[data.xyValueIndex[0]]) {
427
575
  maxXDatum = datum
428
576
  }
429
- if (minYDatum == null || datum.value[1] < minYDatum.value[1]) {
577
+ if (minYDatum == null || yValue < minYDatum.value[data.xyValueIndex[1]]) {
430
578
  minYDatum = datum
431
579
  }
432
- if (maxYDatum == null || datum.value[1] > maxYDatum.value[1]) {
580
+ if (maxYDatum == null || yValue > maxYDatum.value[data.xyValueIndex[1]]) {
433
581
  maxYDatum = datum
434
582
  }
435
583
  }
@@ -447,29 +595,123 @@ export const filteredMinMaxXYDataObservable = ({ visibleComputedLayoutData$, min
447
595
  )
448
596
  }
449
597
 
450
- export const multiValueGraphicTransformObservable = ({ minMaxXY$, filteredMinMaxXYData$, fullDataFormatter$, layout$ }: {
451
- minMaxXY$: Observable<{ minX: number, maxX: number, minY: number, maxY: number }>
452
- filteredMinMaxXYData$: Observable<{
453
- minXDatum: ComputedLayoutDatumMultiValue
454
- maxXDatum: ComputedLayoutDatumMultiValue
455
- minYDatum: ComputedLayoutDatumMultiValue
456
- maxYDatum: ComputedLayoutDatumMultiValue
598
+ // export const visibleComputedRankingDataObservable = ({ visibleComputedData$ }: {
599
+ // visibleComputedData$: Observable<ComputedDatumMultiValue[][]>
600
+ // }) => {
601
+ // return visibleComputedData$.pipe(
602
+ // map(visibleComputedData => visibleComputedData
603
+ // .flat()
604
+ // .map(d => {
605
+ // // 新增總計資料欄位
606
+ // ;(d as any)._sum = d.value.reduce((acc, curr) => acc + curr, 0)
607
+ // return d
608
+ // })
609
+ // .sort((a: any, b: any) => b._sum - a._sum)
610
+ // )
611
+ // )
612
+
613
+ // // const labelAmountLimit$ = combineLatest({
614
+ // // layout: layout$,
615
+ // // textSizePx: textSizePx$,
616
+ // // sortedLabels: sortedLabels$
617
+ // // }).pipe(
618
+ // // switchMap(async (d) => d),
619
+ // // map(data => {
620
+ // // const lineHeight = data.textSizePx * 2 // 2倍行高
621
+ // // const labelAmountLimit = Math.floor(data.layout.height / lineHeight)
622
+ // // return labelAmountLimit
623
+ // // }),
624
+ // // distinctUntilChanged()
625
+ // // )
626
+
627
+ // // return combineLatest({
628
+ // // sortedLabels: sortedLabels$,
629
+ // // labelAmountLimit: labelAmountLimit$
630
+ // // }).pipe(
631
+ // // map(data => {
632
+ // // return data.sortedLabels.slice(0, data.labelAmountLimit)
633
+ // // })
634
+ // // )
635
+
636
+ // }
637
+
638
+ // export const rankingAmountLimitObservable = ({ layout$, textSizePx$ }: {
639
+ // layout$: Observable<Layout>
640
+ // textSizePx$: Observable<number>
641
+ // }) => {
642
+ // return combineLatest({
643
+ // layout: layout$,
644
+ // textSizePx: textSizePx$
645
+ // }).pipe(
646
+ // switchMap(async (d) => d),
647
+ // map(data => {
648
+ // const lineHeight = data.textSizePx * 2 // 2倍行高
649
+ // const labelAmountLimit = Math.floor(data.layout.height / lineHeight)
650
+ // return labelAmountLimit
651
+ // }),
652
+ // distinctUntilChanged()
653
+ // )
654
+ // }
655
+
656
+ // export const rankingScaleObservable = ({ layout$, visibleComputedRankingData$, rankingAmountLimit$ }: {
657
+ // layout$: Observable<Layout>
658
+ // visibleComputedRankingData$: Observable<ComputedDatumMultiValue[]>
659
+ // rankingAmountLimit$: Observable<number>
660
+ // }) => {
661
+ // return combineLatest({
662
+ // layout: layout$,
663
+ // rankingAmountLimit: rankingAmountLimit$,
664
+ // visibleComputedRankingData: visibleComputedRankingData$,
665
+ // }).pipe(
666
+ // switchMap(async (d) => d),
667
+ // map(data => {
668
+ // let labelAmount = 0
669
+ // let lineHeight = 0
670
+ // let totalHeight = 0
671
+ // if (data.visibleComputedRankingData.length > data.rankingAmountLimit) {
672
+ // labelAmount = data.rankingAmountLimit
673
+ // lineHeight = data.layout.height / labelAmount
674
+ // totalHeight = lineHeight * labelAmount // 用全部的數量來算而不是要顯示的數量(要超出圖軸高度)
675
+ // } else {
676
+ // labelAmount = data.visibleComputedRankingData.length
677
+ // lineHeight = data.layout.height / labelAmount
678
+ // totalHeight = data.layout.height
679
+ // }
680
+
681
+ // return createLabelToAxisScale({
682
+ // axisLabels: data.visibleComputedRankingData.map(d => d.label),
683
+ // axisWidth: totalHeight,
684
+ // padding: 0.5
685
+ // })
686
+ // })
687
+ // )
688
+ // }
689
+
690
+ export const graphicTransformObservable = ({ xyMinMax$, xyValueIndex$, filteredXYMinMaxData$, fullDataFormatter$, layout$ }: {
691
+ xyMinMax$: Observable<{ minX: number, maxX: number, minY: number, maxY: number }>
692
+ xyValueIndex$: Observable<[number, number]>
693
+ filteredXYMinMaxData$: Observable<{
694
+ minXDatum: ComputedXYDatumMultiValue
695
+ maxXDatum: ComputedXYDatumMultiValue
696
+ minYDatum: ComputedXYDatumMultiValue
697
+ maxYDatum: ComputedXYDatumMultiValue
457
698
  }>
458
699
  fullDataFormatter$: Observable<DataFormatterTypeMap<'multiValue'>>
459
700
  layout$: Observable<Layout>
460
701
  }): Observable<TransformData> => {
461
702
  const destroy$ = new Subject()
462
703
 
463
- function calcDataAreaTransform ({ minMaxXY, filteredMinMaxXYData, xAxis, yAxis, width, height }: {
464
- minMaxXY: { minX: number, maxX: number, minY: number, maxY: number }
465
- filteredMinMaxXYData: {
466
- minXDatum: ComputedLayoutDatumMultiValue
467
- maxXDatum: ComputedLayoutDatumMultiValue
468
- minYDatum: ComputedLayoutDatumMultiValue
469
- maxYDatum: ComputedLayoutDatumMultiValue
704
+ function calcDataAreaTransform ({ xyMinMax, xyValueIndex, filteredXYMinMaxData, xAxis, yAxis, width, height }: {
705
+ xyMinMax: { minX: number, maxX: number, minY: number, maxY: number }
706
+ xyValueIndex: [number, number]
707
+ filteredXYMinMaxData: {
708
+ minXDatum: ComputedXYDatumMultiValue
709
+ maxXDatum: ComputedXYDatumMultiValue
710
+ minYDatum: ComputedXYDatumMultiValue
711
+ maxYDatum: ComputedXYDatumMultiValue
470
712
  }
471
- xAxis: DataFormatterAxis
472
- yAxis: DataFormatterAxis
713
+ xAxis: DataFormatterXYAxis
714
+ yAxis: DataFormatterXYAxis
473
715
  width: number
474
716
  height: number
475
717
  }): TransformData {
@@ -509,13 +751,13 @@ export const multiValueGraphicTransformObservable = ({ minMaxXY$, filteredMinMax
509
751
  // let filteredMinY = 0
510
752
  // let filteredMaxY = 0
511
753
  // let [minY, maxY] = getMinMax(flatData.map(d => d.value[1]))
512
- // console.log('filteredMinMaxXYData', filteredMinMaxXYData)
513
- let { minX, maxX, minY, maxY } = minMaxXY
754
+ // console.log('filteredXYMinMaxData', filteredXYMinMaxData)
755
+ let { minX, maxX, minY, maxY } = xyMinMax
514
756
  // console.log({ minX, maxX, minY, maxY })
515
- let filteredMinX = filteredMinMaxXYData.minXDatum.value[0] ?? 0
516
- let filteredMaxX = filteredMinMaxXYData.maxXDatum.value[0] ?? 0
517
- let filteredMinY = filteredMinMaxXYData.minYDatum.value[1] ?? 0
518
- let filteredMaxY = filteredMinMaxXYData.maxYDatum.value[1] ?? 0
757
+ let filteredMinX = filteredXYMinMaxData.minXDatum.value[xyValueIndex[0]] ?? 0
758
+ let filteredMaxX = filteredXYMinMaxData.maxXDatum.value[xyValueIndex[0]] ?? 0
759
+ let filteredMinY = filteredXYMinMaxData.minYDatum.value[xyValueIndex[1]] ?? 0
760
+ let filteredMaxY = filteredXYMinMaxData.maxYDatum.value[xyValueIndex[1]] ?? 0
519
761
 
520
762
  // if (yAxis.scaleDomain[0] === 'auto' && filteredMinY > 0) {
521
763
  // filteredMinY = 0
@@ -594,24 +836,26 @@ export const multiValueGraphicTransformObservable = ({ minMaxXY$, filteredMinMax
594
836
 
595
837
  return new Observable(subscriber => {
596
838
  combineLatest({
597
- minMaxXY: minMaxXY$,
598
- filteredMinMaxXYData: filteredMinMaxXYData$,
839
+ xyMinMax: xyMinMax$,
840
+ xyValueIndex: xyValueIndex$,
841
+ filteredXYMinMaxData: filteredXYMinMaxData$,
599
842
  fullDataFormatter: fullDataFormatter$,
600
843
  layout: layout$
601
844
  }).pipe(
602
845
  takeUntil(destroy$),
603
846
  switchMap(async (d) => d),
604
847
  ).subscribe(data => {
605
- if (!data.filteredMinMaxXYData.minXDatum || !data.filteredMinMaxXYData.maxXDatum
606
- || data.filteredMinMaxXYData.minXDatum.value[0] == null || data.filteredMinMaxXYData.maxXDatum.value[0] == null
607
- || !data.filteredMinMaxXYData.minYDatum || !data.filteredMinMaxXYData.maxYDatum
608
- || data.filteredMinMaxXYData.minYDatum.value[1] == null || data.filteredMinMaxXYData.maxYDatum.value[1] == null
848
+ if (!data.filteredXYMinMaxData.minXDatum || !data.filteredXYMinMaxData.maxXDatum
849
+ || data.filteredXYMinMaxData.minXDatum.value[data.xyValueIndex[0]] == null || data.filteredXYMinMaxData.maxXDatum.value[data.xyValueIndex[0]] == null
850
+ || !data.filteredXYMinMaxData.minYDatum || !data.filteredXYMinMaxData.maxYDatum
851
+ || data.filteredXYMinMaxData.minYDatum.value[data.xyValueIndex[1]] == null || data.filteredXYMinMaxData.maxYDatum.value[data.xyValueIndex[1]] == null
609
852
  ) {
610
853
  return
611
854
  }
612
855
  const dataAreaTransformData = calcDataAreaTransform({
613
- minMaxXY: data.minMaxXY,
614
- filteredMinMaxXYData: data.filteredMinMaxXYData,
856
+ xyMinMax: data.xyMinMax,
857
+ xyValueIndex: data.xyValueIndex,
858
+ filteredXYMinMaxData: data.filteredXYMinMaxData,
615
859
  xAxis: data.fullDataFormatter.xAxis,
616
860
  yAxis: data.fullDataFormatter.yAxis,
617
861
  width: data.layout.width,
@@ -629,34 +873,172 @@ export const multiValueGraphicTransformObservable = ({ minMaxXY$, filteredMinMax
629
873
  })
630
874
  }
631
875
 
632
- export const multiValueGraphicReverseScaleObservable = ({ multiValueContainerPosition$, multiValueGraphicTransform$ }: {
633
- multiValueContainerPosition$: Observable<ContainerPositionScaled[]>
876
+ export const graphicReverseScaleObservable = ({ containerPosition$, graphicTransform$ }: {
877
+ containerPosition$: Observable<ContainerPositionScaled[]>
634
878
  // multiValueAxesTransform$: Observable<TransformData>
635
- multiValueGraphicTransform$: Observable<TransformData>
879
+ graphicTransform$: Observable<TransformData>
636
880
  }): Observable<[number, number][]> => {
637
881
  return combineLatest({
638
- multiValueContainerPosition: multiValueContainerPosition$,
882
+ containerPosition: containerPosition$,
639
883
  // multiValueAxesTransform: multiValueAxesTransform$,
640
- multiValueGraphicTransform: multiValueGraphicTransform$,
884
+ graphicTransform: graphicTransform$,
641
885
  }).pipe(
642
886
  switchMap(async (d) => d),
643
887
  map(data => {
644
888
  // if (data.multiValueAxesTransform.rotate == 0 || data.multiValueAxesTransform.rotate == 180) {
645
- return data.multiValueContainerPosition.map((series, seriesIndex) => {
889
+ return data.containerPosition.map((series, seriesIndex) => {
646
890
  return [
647
- 1 / data.multiValueGraphicTransform.scale[0] / data.multiValueContainerPosition[seriesIndex].scale[0],
648
- 1 / data.multiValueGraphicTransform.scale[1] / data.multiValueContainerPosition[seriesIndex].scale[1],
891
+ 1 / data.graphicTransform.scale[0] / data.containerPosition[seriesIndex].scale[0],
892
+ 1 / data.graphicTransform.scale[1] / data.containerPosition[seriesIndex].scale[1],
649
893
  ]
650
894
  })
651
895
  // } else {
652
- // return data.multiValueContainerPosition.map((series, seriesIndex) => {
896
+ // return data.containerPosition.map((series, seriesIndex) => {
653
897
  // // 由於有垂直的旋轉,所以外層 (container) x和y的scale要互換
654
898
  // return [
655
- // 1 / data.multiValueGraphicTransform.scale[0] / data.multiValueContainerPosition[seriesIndex].scale[1],
656
- // 1 / data.multiValueGraphicTransform.scale[1] / data.multiValueContainerPosition[seriesIndex].scale[0],
899
+ // 1 / data.graphicTransform.scale[0] / data.containerPosition[seriesIndex].scale[1],
900
+ // 1 / data.graphicTransform.scale[1] / data.containerPosition[seriesIndex].scale[0],
657
901
  // ]
658
902
  // })
659
903
  // }
660
904
  }),
661
905
  )
662
- }
906
+ }
907
+
908
+ // X 軸圖軸 - 用 value[index]
909
+ export const xScaleObservable = ({ visibleComputedSumData$, fullDataFormatter$, filteredXYMinMaxData$, containerSize$ }: {
910
+ visibleComputedSumData$: Observable<ComputedDatumMultiValue[][]>
911
+ fullDataFormatter$: Observable<DataFormatterMultiValue>
912
+ filteredXYMinMaxData$: Observable<{
913
+ minXDatum: ComputedXYDatumMultiValue
914
+ maxXDatum: ComputedXYDatumMultiValue
915
+ minYDatum: ComputedXYDatumMultiValue
916
+ maxYDatum: ComputedXYDatumMultiValue
917
+ }>
918
+ // layout$: Observable<Layout>
919
+ containerSize$: Observable<ContainerSize>
920
+ }) => {
921
+ return combineLatest({
922
+ visibleComputedSumData: visibleComputedSumData$,
923
+ fullDataFormatter: fullDataFormatter$,
924
+ containerSize: containerSize$,
925
+ // xyMinMax: xyMinMax$
926
+ filteredXYMinMaxData: filteredXYMinMaxData$
927
+ }).pipe(
928
+ switchMap(async (d) => d),
929
+ map(data => {
930
+ const valueIndex = data.fullDataFormatter.xAxis.valueIndex
931
+ if (!data.filteredXYMinMaxData.minXDatum || !data.filteredXYMinMaxData.maxXDatum
932
+ // || data.filteredXYMinMaxData.minXDatum.value[valueIndex] == null || data.filteredXYMinMaxData.maxXDatum.value[valueIndex] == null
933
+ ) {
934
+ return
935
+ }
936
+ let maxValue: number | null = data.filteredXYMinMaxData.maxXDatum.value[valueIndex]
937
+ let minValue: number | null = data.filteredXYMinMaxData.minXDatum.value[valueIndex]
938
+ if (maxValue === minValue && maxValue === 0) {
939
+ // 避免最大及最小值同等於 0 造成無法計算scale
940
+ maxValue = 1
941
+ }
942
+
943
+ const xScale: d3.ScaleLinear<number, number> = createValueToAxisScale({
944
+ maxValue,
945
+ minValue,
946
+ axisWidth: data.containerSize.width,
947
+ scaleDomain: data.fullDataFormatter.xAxis.scaleDomain,
948
+ scaleRange: data.fullDataFormatter.xAxis.scaleRange,
949
+ })
950
+ return xScale
951
+ })
952
+ )
953
+ }
954
+
955
+ // X 軸圖軸 - 用所有 valueIndex 加總資料
956
+ export const xSumScaleObservable = ({ fullDataFormatter$, filteredXYMinMaxData$, containerSize$ }: {
957
+ // valueIndex$: Observable<number>
958
+ fullDataFormatter$: Observable<DataFormatterMultiValue>
959
+ filteredXYMinMaxData$: Observable<{
960
+ minXDatum: ComputedXYDatumMultiValue
961
+ maxXDatum: ComputedXYDatumMultiValue
962
+ minYDatum: ComputedXYDatumMultiValue
963
+ maxYDatum: ComputedXYDatumMultiValue
964
+ }>
965
+ // layout$: Observable<Layout>
966
+ containerSize$: Observable<ContainerSize>
967
+ }) => {
968
+ return combineLatest({
969
+ // valueIndex: valueIndex$,
970
+ fullDataFormatter: fullDataFormatter$,
971
+ containerSize: containerSize$,
972
+ // xyMinMax: xyMinMax$
973
+ filteredXYMinMaxData: filteredXYMinMaxData$
974
+ }).pipe(
975
+ switchMap(async (d) => d),
976
+ map(data => {
977
+ const valueIndex = data.fullDataFormatter.xAxis.valueIndex
978
+ if (!data.filteredXYMinMaxData.minXDatum || !data.filteredXYMinMaxData.maxXDatum
979
+ // || data.filteredXYMinMaxData.minXDatum.value[valueIndex] == null || data.filteredXYMinMaxData.maxXDatum.value[valueIndex] == null
980
+ ) {
981
+ return
982
+ }
983
+ let maxValue: number | null = data.filteredXYMinMaxData.maxXDatum.value[valueIndex]
984
+ let minValue: number | null = data.filteredXYMinMaxData.minXDatum.value[valueIndex]
985
+ if (maxValue === minValue && maxValue === 0) {
986
+ // 避免最大及最小值同等於 0 造成無法計算scale
987
+ maxValue = 1
988
+ }
989
+
990
+ const xScale: d3.ScaleLinear<number, number> = createValueToAxisScale({
991
+ maxValue,
992
+ minValue,
993
+ axisWidth: data.containerSize.width,
994
+ scaleDomain: data.fullDataFormatter.xAxis.scaleDomain,
995
+ scaleRange: data.fullDataFormatter.xAxis.scaleRange,
996
+ })
997
+ return xScale
998
+ })
999
+ )
1000
+ }
1001
+
1002
+ export const yScaleObservable = ({ fullDataFormatter$, filteredXYMinMaxData$, containerSize$ }: {
1003
+ fullDataFormatter$: Observable<DataFormatterMultiValue>
1004
+ filteredXYMinMaxData$: Observable<{
1005
+ minXDatum: ComputedXYDatumMultiValue
1006
+ maxXDatum: ComputedXYDatumMultiValue
1007
+ minYDatum: ComputedXYDatumMultiValue
1008
+ maxYDatum: ComputedXYDatumMultiValue
1009
+ }>
1010
+ containerSize$: Observable<ContainerSize>
1011
+ }) => {
1012
+ return combineLatest({
1013
+ fullDataFormatter: fullDataFormatter$,
1014
+ containerSize: containerSize$,
1015
+ // xyMinMax: observer.xyMinMax$
1016
+ filteredXYMinMaxData: filteredXYMinMaxData$
1017
+ }).pipe(
1018
+ switchMap(async (d) => d),
1019
+ map(data => {
1020
+ const valueIndex = data.fullDataFormatter.yAxis.valueIndex
1021
+ if (!data.filteredXYMinMaxData.minYDatum || !data.filteredXYMinMaxData.maxYDatum
1022
+ || data.filteredXYMinMaxData.minYDatum.value[valueIndex] == null || data.filteredXYMinMaxData.maxYDatum.value[valueIndex] == null
1023
+ ) {
1024
+ return
1025
+ }
1026
+ let maxValue = data.filteredXYMinMaxData.maxYDatum.value[valueIndex]
1027
+ let minValue = data.filteredXYMinMaxData.minYDatum.value[valueIndex]
1028
+ if (maxValue === minValue && maxValue === 0) {
1029
+ // 避免最大及最小值同等於 0 造成無法計算scale
1030
+ maxValue = 1
1031
+ }
1032
+
1033
+ const yScale: d3.ScaleLinear<number, number> = createValueToAxisScale({
1034
+ maxValue,
1035
+ minValue,
1036
+ axisWidth: data.containerSize.height,
1037
+ scaleDomain: data.fullDataFormatter.yAxis.scaleDomain,
1038
+ scaleRange: data.fullDataFormatter.yAxis.scaleRange,
1039
+ reverse: true
1040
+ })
1041
+ return yScale
1042
+ })
1043
+ )
1044
+ }