@orbcharts/core 3.0.0-beta.3 → 3.0.0-beta.4

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 (77) hide show
  1. package/LICENSE +200 -200
  2. package/dist/orbcharts-core.es.js +2623 -2338
  3. package/dist/orbcharts-core.umd.js +4 -4
  4. package/dist/src/utils/d3Scale.d.ts +28 -0
  5. package/dist/src/utils/gridObservables.d.ts +29 -19
  6. package/dist/src/utils/index.d.ts +1 -1
  7. package/dist/src/utils/multiGridObservables.d.ts +2 -2
  8. package/dist/src/utils/multiValueObservables.d.ts +73 -0
  9. package/dist/src/utils/orbchartsUtils.d.ts +19 -5
  10. package/dist/src/utils/seriesObservables.d.ts +4 -4
  11. package/dist/src/utils/treeObservables.d.ts +2 -5
  12. package/lib/core-types.ts +7 -7
  13. package/package.json +42 -42
  14. package/src/AbstractChart.ts +57 -57
  15. package/src/GridChart.ts +24 -24
  16. package/src/MultiGridChart.ts +24 -24
  17. package/src/MultiValueChart.ts +24 -24
  18. package/src/RelationshipChart.ts +24 -24
  19. package/src/SeriesChart.ts +24 -24
  20. package/src/TreeChart.ts +24 -24
  21. package/src/base/createBaseChart.ts +505 -505
  22. package/src/base/createBasePlugin.ts +153 -153
  23. package/src/base/validators/chartOptionsValidator.ts +23 -23
  24. package/src/base/validators/chartParamsValidator.ts +133 -133
  25. package/src/base/validators/elementValidator.ts +13 -13
  26. package/src/base/validators/pluginsValidator.ts +14 -14
  27. package/src/defaults.ts +235 -232
  28. package/src/defineGridPlugin.ts +3 -3
  29. package/src/defineMultiGridPlugin.ts +3 -3
  30. package/src/defineMultiValuePlugin.ts +3 -3
  31. package/src/defineNoneDataPlugin.ts +4 -4
  32. package/src/defineRelationshipPlugin.ts +3 -3
  33. package/src/defineSeriesPlugin.ts +3 -3
  34. package/src/defineTreePlugin.ts +3 -3
  35. package/src/grid/computedDataFn.ts +129 -129
  36. package/src/grid/contextObserverCallback.ts +176 -155
  37. package/src/grid/dataFormatterValidator.ts +101 -101
  38. package/src/grid/dataValidator.ts +12 -12
  39. package/src/index.ts +20 -20
  40. package/src/multiGrid/computedDataFn.ts +123 -123
  41. package/src/multiGrid/contextObserverCallback.ts +41 -41
  42. package/src/multiGrid/dataFormatterValidator.ts +115 -115
  43. package/src/multiGrid/dataValidator.ts +12 -12
  44. package/src/multiValue/computedDataFn.ts +110 -176
  45. package/src/multiValue/contextObserverCallback.ts +160 -12
  46. package/src/multiValue/dataFormatterValidator.ts +9 -9
  47. package/src/multiValue/dataValidator.ts +9 -9
  48. package/src/relationship/computedDataFn.ts +125 -125
  49. package/src/relationship/contextObserverCallback.ts +12 -12
  50. package/src/relationship/dataFormatterValidator.ts +9 -9
  51. package/src/relationship/dataValidator.ts +9 -9
  52. package/src/series/computedDataFn.ts +88 -88
  53. package/src/series/contextObserverCallback.ts +100 -100
  54. package/src/series/dataFormatterValidator.ts +41 -41
  55. package/src/series/dataValidator.ts +12 -12
  56. package/src/tree/computedDataFn.ts +129 -130
  57. package/src/tree/contextObserverCallback.ts +58 -61
  58. package/src/tree/dataFormatterValidator.ts +13 -13
  59. package/src/tree/dataValidator.ts +13 -13
  60. package/src/utils/commonUtils.ts +55 -55
  61. package/src/utils/d3Scale.ts +198 -0
  62. package/src/utils/errorMessage.ts +42 -42
  63. package/src/utils/gridObservables.ts +671 -614
  64. package/src/utils/index.ts +9 -9
  65. package/src/utils/multiGridObservables.ts +392 -366
  66. package/src/utils/multiValueObservables.ts +643 -0
  67. package/src/utils/observables.ts +219 -219
  68. package/src/utils/orbchartsUtils.ts +377 -352
  69. package/src/utils/seriesObservables.ts +175 -175
  70. package/src/utils/treeObservables.ts +105 -94
  71. package/src/utils/validator.ts +126 -125
  72. package/tsconfig.base.json +13 -13
  73. package/tsconfig.json +2 -2
  74. package/vite-env.d.ts +6 -6
  75. package/vite.config.js +22 -22
  76. package/dist/src/utils/d3Utils.d.ts +0 -19
  77. package/src/utils/d3Utils.ts +0 -108
@@ -0,0 +1,643 @@
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
+ ComputedDataMultiValue,
20
+ ComputedDatumMultiValue,
21
+ DataFormatterTypeMap,
22
+ DataFormatterMultiValue,
23
+ DataFormatterAxis,
24
+ ComputedLayoutDatumMultiValue,
25
+ ComputedLayoutDataMultiValue,
26
+ ContainerPositionScaled,
27
+ HighlightTarget,
28
+ Layout,
29
+ TransformData } from '../../lib/core-types'
30
+ import { getMinAndMax, getMinAndMaxMultiValue } from './orbchartsUtils'
31
+ import { createValueToAxisScale, createLabelToAxisScale, createAxisToLabelIndexScale } from './d3Scale'
32
+ import { calcGridContainerLayout } from './orbchartsUtils'
33
+
34
+ export const minMaxXYObservable = ({ computedData$ }: { computedData$: Observable<ComputedDataTypeMap<'multiValue'>> }) => {
35
+ return computedData$.pipe(
36
+ map(data => {
37
+ const flatData = data.flat()
38
+ const [minX, maxX] = getMinAndMax(flatData.map(d => d.value[0]))
39
+ const [minY, maxY] = getMinAndMax(flatData.map(d => d.value[1]))
40
+ return { minX, maxX, minY, maxY }
41
+ })
42
+ )
43
+ }
44
+
45
+ export const multiValueComputedLayoutDataObservable = ({ computedData$, minMaxXY$, fullDataFormatter$, layout$ }: {
46
+ computedData$: Observable<ComputedDataTypeMap<'multiValue'>>
47
+ minMaxXY$: Observable<{ minX: number, maxX: number, minY: number, maxY: number }>
48
+ fullDataFormatter$: Observable<DataFormatterTypeMap<'multiValue'>>
49
+ layout$: Observable<Layout>
50
+ }): Observable<ComputedLayoutDataMultiValue> => {
51
+
52
+ // 未篩選範圍前的 scale
53
+ function createOriginXScale (minMaxXY: { minX: number, maxX: number, minY: number, maxY: number }, layout: Layout) {
54
+ const valueScale: d3.ScaleLinear<number, number> = createValueToAxisScale({
55
+ maxValue: minMaxXY.maxX,
56
+ minValue: minMaxXY.minX,
57
+ axisWidth: layout.width,
58
+ scaleDomain: ['auto', 'auto'], // 不使用dataFormatter設定 --> 以0為基準到最大或最小值為範圍( * 如果是使用[minValue, maxValue]的話,在兩者很接近的情況下有可能造成scale倍率過高而svg變型時失真的情況)
59
+ scaleRange: [0, 1] // 不使用dataFormatter設定
60
+ })
61
+
62
+ return valueScale
63
+ }
64
+
65
+ // 未篩選範圍及visible前的 scale
66
+ function createOriginYScale (minMaxXY: { minX: number, maxX: number, minY: number, maxY: number }, layout: Layout) {
67
+ const valueScale: d3.ScaleLinear<number, number> = createValueToAxisScale({
68
+ maxValue: minMaxXY.maxY,
69
+ minValue: minMaxXY.minY,
70
+ axisWidth: layout.height,
71
+ scaleDomain: ['auto', 'auto'], // 不使用dataFormatter設定 --> 以0為基準到最大或最小值為範圍( * 如果是使用[minValue, maxValue]的話,在兩者很接近的情況下有可能造成scale倍率過高而svg變型時失真的情況)
72
+ scaleRange: [0, 1], // 不使用dataFormatter設定
73
+ reverse: true
74
+ })
75
+
76
+ return valueScale
77
+ }
78
+
79
+ return combineLatest({
80
+ computedData: computedData$,
81
+ minMaxXY: minMaxXY$,
82
+ fullDataFormatter: fullDataFormatter$,
83
+ layout: layout$
84
+ }).pipe(
85
+ switchMap(async d => d),
86
+ map(data => {
87
+
88
+ const xScale = createOriginXScale(data.minMaxXY, data.layout)
89
+ const yScale = createOriginYScale(data.minMaxXY, data.layout)
90
+
91
+ return data.computedData
92
+ .map((categoryData, categoryIndex) => {
93
+ return categoryData.map((datum, datumIndex) => {
94
+ return {
95
+ ...datum,
96
+ axisX: xScale(datum.value[0] ?? 0),
97
+ // axisY: data.layout.height - yScale(datum.value[1] ?? 0), // y軸的繪圖座標是從上到下,所以反轉
98
+ axisY: yScale(datum.value[1] ?? 0), // y軸的繪圖座標是從上到下,所以反轉
99
+ }
100
+ })
101
+ })
102
+ })
103
+ )
104
+ }
105
+
106
+ // export const multiValueAxesTransformObservable = ({ fullDataFormatter$, layout$ }: {
107
+ // fullDataFormatter$: Observable<DataFormatterTypeMap<'multiValue'>>
108
+ // layout$: Observable<Layout>
109
+ // }): Observable<TransformData> => {
110
+ // const destroy$ = new Subject()
111
+
112
+ // function calcAxesTransform ({ xAxis, yAxis, width, height }: {
113
+ // xAxis: DataFormatterAxis,
114
+ // yAxis: DataFormatterAxis,
115
+ // width: number,
116
+ // height: number
117
+ // }): TransformData {
118
+ // if (!xAxis || !yAxis) {
119
+ // return {
120
+ // translate: [0, 0],
121
+ // scale: [1, 1],
122
+ // rotate: 0,
123
+ // rotateX: 0,
124
+ // rotateY: 0,
125
+ // value: ''
126
+ // }
127
+ // }
128
+ // // const width = size.width - fullChartParams.layout.left - fullChartParams.layout.right
129
+ // // const height = size.height - fullChartParams.layout.top - fullChartParams.layout.bottom
130
+ // let translateX = 0
131
+ // let translateY = height
132
+ // let rotate = 0
133
+ // let rotateX = 180
134
+ // let rotateY = 0
135
+
136
+ // return {
137
+ // translate: [translateX, translateY],
138
+ // scale: [1, 1],
139
+ // rotate,
140
+ // rotateX,
141
+ // rotateY,
142
+ // value: `translate(${translateX}px, ${translateY}px) rotate(${rotate}deg) rotateX(${rotateX}deg) rotateY(${rotateY}deg)`
143
+ // }
144
+ // }
145
+
146
+ // return new Observable(subscriber => {
147
+ // combineLatest({
148
+ // fullDataFormatter: fullDataFormatter$,
149
+ // layout: layout$
150
+ // }).pipe(
151
+ // takeUntil(destroy$),
152
+ // switchMap(async (d) => d),
153
+ // ).subscribe(data => {
154
+ // const axesTransformData = calcAxesTransform({
155
+ // xAxis: data.fullDataFormatter.xAxis,
156
+ // yAxis: data.fullDataFormatter.yAxis,
157
+ // width: data.layout.width,
158
+ // height: data.layout.height
159
+ // })
160
+
161
+ // subscriber.next(axesTransformData)
162
+ // })
163
+
164
+ // return function unscbscribe () {
165
+ // destroy$.next(undefined)
166
+ // }
167
+ // })
168
+ // }
169
+
170
+
171
+ // export const multiValueAxesReverseTransformObservable = ({ multiValueAxesTransform$ }: {
172
+ // multiValueAxesTransform$: Observable<TransformData>
173
+ // }): Observable<TransformData> => {
174
+ // return multiValueAxesTransform$.pipe(
175
+ // map(d => {
176
+ // // const translate: [number, number] = [d.translate[0] * -1, d.translate[1] * -1]
177
+ // const translate: [number, number] = [0, 0] // 無需逆轉
178
+ // const scale: [number, number] = [1 / d.scale[0], 1 / d.scale[1]]
179
+ // const rotate = d.rotate * -1
180
+ // const rotateX = d.rotateX * -1
181
+ // const rotateY = d.rotateY * -1
182
+ // return {
183
+ // translate,
184
+ // scale,
185
+ // rotate,
186
+ // rotateX,
187
+ // rotateY,
188
+ // value: `translate(${translate[0]}px, ${translate[1]}px) rotateX(${rotateX}deg) rotateY(${rotateY}deg) rotate(${rotate}deg)`
189
+ // }
190
+ // }),
191
+ // )
192
+ // }
193
+
194
+
195
+
196
+ // export const multiValueAxesSizeObservable = ({ fullDataFormatter$, layout$ }: {
197
+ // fullDataFormatter$: Observable<DataFormatterMultiValue>
198
+ // layout$: Observable<Layout>
199
+ // }): Observable<{
200
+ // width: number;
201
+ // height: number;
202
+ // }> => {
203
+ // const destroy$ = new Subject()
204
+
205
+ // function calcAxesSize ({ xAxisPosition, yAxisPosition, width, height }: {
206
+ // xAxisPosition: AxisPosition
207
+ // yAxisPosition: AxisPosition
208
+ // width: number
209
+ // height: number
210
+ // }) {
211
+ // if ((xAxisPosition === 'bottom' || xAxisPosition === 'top') && (yAxisPosition === 'left' || yAxisPosition === 'right')) {
212
+ // return { width, height }
213
+ // } else if ((xAxisPosition === 'left' || xAxisPosition === 'right') && (yAxisPosition === 'bottom' || yAxisPosition === 'top')) {
214
+ // return {
215
+ // width: height,
216
+ // height: width
217
+ // }
218
+ // } else {
219
+ // // default
220
+ // return { width, height }
221
+ // }
222
+ // }
223
+
224
+ // return new Observable(subscriber => {
225
+ // combineLatest({
226
+ // fullDataFormatter: fullDataFormatter$,
227
+ // layout: layout$
228
+ // }).pipe(
229
+ // takeUntil(destroy$),
230
+ // switchMap(async (d) => d),
231
+ // ).subscribe(data => {
232
+
233
+ // const axisSize = calcAxesSize({
234
+ // xAxisPosition: 'bottom',
235
+ // yAxisPosition: 'left',
236
+ // width: data.layout.width,
237
+ // height: data.layout.height,
238
+ // })
239
+
240
+ // subscriber.next(axisSize)
241
+
242
+ // return function unsubscribe () {
243
+ // destroy$.next(undefined)
244
+ // }
245
+ // })
246
+ // })
247
+ // }
248
+
249
+ // export const multiValueHighlightObservable = ({ computedData$, fullChartParams$, event$ }: {
250
+ // computedData$: Observable<ComputedDataTypeMap<'multiValue'>>
251
+ // fullChartParams$: Observable<ChartParams>
252
+ // event$: Subject<any>
253
+ // }): Observable<string[]> => {
254
+ // const datumList$ = computedData$.pipe(
255
+ // map(d => d.flat())
256
+ // )
257
+ // return highlightObservable ({ datumList$, fullChartParams$, event$ })
258
+ // }
259
+
260
+ export const multiValueCategoryLabelsObservable = ({ computedData$, fullDataFormatter$ }: {
261
+ computedData$: Observable<ComputedDataTypeMap<'multiValue'>>
262
+ fullDataFormatter$: Observable<DataFormatterTypeMap<'multiValue'>>
263
+ }) => {
264
+ return computedData$.pipe(
265
+ map(data => {
266
+ return data
267
+ .map(d => d[0] ? d[0].categoryLabel : '')
268
+ // .filter(d => d != null && d != '')
269
+ }),
270
+ distinctUntilChanged((a, b) => {
271
+ return JSON.stringify(a).length === JSON.stringify(b).length
272
+ }),
273
+ )
274
+ }
275
+
276
+ export const multiValueVisibleComputedDataObservable = ({ computedData$ }: { computedData$: Observable<ComputedDataTypeMap<'multiValue'>> }) => {
277
+ return computedData$.pipe(
278
+ map(data => {
279
+ return data
280
+ .map(categoryData => {
281
+ return categoryData.filter(d => d.visible == true)
282
+ })
283
+ .filter(categoryData => {
284
+ return categoryData.length > 0
285
+ })
286
+ })
287
+ )
288
+ }
289
+
290
+ export const multiValueVisibleComputedLayoutDataObservable = ({ computedLayoutData$ }: { computedLayoutData$: Observable<ComputedLayoutDataMultiValue> }) => {
291
+ return computedLayoutData$.pipe(
292
+ map(data => {
293
+ return data
294
+ .map(categoryData => {
295
+ return categoryData.filter(d => d.visible == true)
296
+ })
297
+ .filter(categoryData => {
298
+ return categoryData.length > 0
299
+ })
300
+ })
301
+ )
302
+ }
303
+
304
+ // 所有container位置(對應series)
305
+ export const multiValueContainerPositionObservable = ({ computedData$, fullDataFormatter$, layout$ }: {
306
+ computedData$: Observable<ComputedDataTypeMap<'multiValue'>>
307
+ fullDataFormatter$: Observable<DataFormatterTypeMap<'multiValue'>>
308
+ layout$: Observable<Layout>
309
+ }): Observable<ContainerPositionScaled[]> => {
310
+
311
+ const multiValueContainerPosition$ = combineLatest({
312
+ computedData: computedData$,
313
+ fullDataFormatter: fullDataFormatter$,
314
+ layout: layout$,
315
+ }).pipe(
316
+ switchMap(async (d) => d),
317
+ map(data => {
318
+
319
+ if (data.fullDataFormatter.separateCategory) {
320
+ // -- 依slotIndexes計算 --
321
+ return calcGridContainerLayout(data.layout, data.fullDataFormatter.container, data.computedData.length)
322
+ // return data.computedData.map((seriesData, seriesIndex) => {
323
+ // const columnIndex = seriesIndex % data.fullDataFormatter.container.columnAmount
324
+ // const rowIndex = Math.floor(seriesIndex / data.fullDataFormatter.container.columnAmount)
325
+ // const { translate, scale } = calcMultiValueContainerPosition(data.layout, data.fullDataFormatter.container, rowIndex, columnIndex)
326
+ // return {
327
+ // slotIndex: seriesIndex,
328
+ // rowIndex,
329
+ // columnIndex,
330
+ // translate,
331
+ // scale,
332
+ // }
333
+ // })
334
+ } else {
335
+ // -- 無拆分 --
336
+ const multiValueContainerPositionArr = calcGridContainerLayout(data.layout, data.fullDataFormatter.container, 1)
337
+ return data.computedData.map((d, i) => multiValueContainerPositionArr[0]) // 每個series相同位置
338
+ // const columnIndex = 0
339
+ // const rowIndex = 0
340
+ // return data.computedData.map((seriesData, seriesIndex) => {
341
+ // const { translate, scale } = calcMultiValueContainerPosition(data.layout, data.fullDataFormatter.container, rowIndex, columnIndex)
342
+ // return {
343
+ // slotIndex: 0,
344
+ // rowIndex,
345
+ // columnIndex,
346
+ // translate,
347
+ // scale,
348
+ // }
349
+ // })
350
+ }
351
+ })
352
+ )
353
+
354
+ return multiValueContainerPosition$
355
+ }
356
+
357
+
358
+ export const filteredMinMaxXYDataObservable = ({ visibleComputedLayoutData$, minMaxXY$, fullDataFormatter$ }: {
359
+ visibleComputedLayoutData$: Observable<ComputedLayoutDataMultiValue>
360
+ minMaxXY$: Observable<{ minX: number, maxX: number, minY: number, maxY: number }>
361
+ fullDataFormatter$: Observable<DataFormatterTypeMap<'multiValue'>>
362
+ }) => {
363
+ return combineLatest({
364
+ visibleComputedLayoutData: visibleComputedLayoutData$,
365
+ minMaxXY: minMaxXY$,
366
+ fullDataFormatter: fullDataFormatter$,
367
+ }).pipe(
368
+ map(data => {
369
+ // 所有可見資料依 dataFormatter 的 scale 設定篩選出最大小值
370
+ const { minX, maxX, minY, maxY } = (() => {
371
+
372
+ let { minX, maxX, minY, maxY } = data.minMaxXY
373
+
374
+ if (data.fullDataFormatter.xAxis.scaleDomain[0] === 'auto' && minX > 0) {
375
+ minX = 0
376
+ } else if (typeof data.fullDataFormatter.xAxis.scaleDomain[0] === 'number') {
377
+ minX = data.fullDataFormatter.xAxis.scaleDomain[0] as number
378
+ }
379
+ if (data.fullDataFormatter.xAxis.scaleDomain[1] === 'auto' && maxX < 0) {
380
+ maxX = 0
381
+ } else if (typeof data.fullDataFormatter.xAxis.scaleDomain[1] === 'number') {
382
+ maxX = data.fullDataFormatter.xAxis.scaleDomain[1] as number
383
+ }
384
+ if (data.fullDataFormatter.yAxis.scaleDomain[0] === 'auto' && minY > 0) {
385
+ minY = 0
386
+ } else if (typeof data.fullDataFormatter.yAxis.scaleDomain[0] === 'number') {
387
+ minY = data.fullDataFormatter.yAxis.scaleDomain[0] as number
388
+ }
389
+ if (data.fullDataFormatter.yAxis.scaleDomain[1] === 'auto' && maxY < 0) {
390
+ maxY = 0
391
+ } else if (typeof data.fullDataFormatter.yAxis.scaleDomain[1] === 'number') {
392
+ maxY = data.fullDataFormatter.yAxis.scaleDomain[1] as number
393
+ }
394
+
395
+ return { minX, maxX, minY, maxY }
396
+ })()
397
+ // console.log({ minX, maxX, minY, maxY })
398
+ let datumList: ComputedLayoutDatumMultiValue[] = []
399
+ let minXDatum: ComputedLayoutDatumMultiValue | null = null
400
+ let maxXDatum: ComputedLayoutDatumMultiValue | null = null
401
+ let minYDatum: ComputedLayoutDatumMultiValue | null = null
402
+ let maxYDatum: ComputedLayoutDatumMultiValue | null = null
403
+ // console.log('data.visibleComputedLayoutData', data.visibleComputedLayoutData)
404
+ // minX, maxX, minY, maxY 範圍內的最大最小值資料
405
+
406
+ for (let categoryData of data.visibleComputedLayoutData) {
407
+ for (let datum of categoryData) {
408
+ // 比較矩形範圍(所以 minX, maxX, minY, maxY 要同時比較)
409
+ if (datum.value[0] >= minX && datum.value[0] <= maxX && datum.value[1] >= minY && datum.value[1] <= maxY) {
410
+ datumList.push(datum)
411
+ if (minXDatum == null || datum.value[0] < minXDatum.value[0]) {
412
+ minXDatum = datum
413
+ }
414
+ if (maxXDatum == null || datum.value[0] > maxXDatum.value[0]) {
415
+ maxXDatum = datum
416
+ }
417
+ if (minYDatum == null || datum.value[1] < minYDatum.value[1]) {
418
+ minYDatum = datum
419
+ }
420
+ if (maxYDatum == null || datum.value[1] > maxYDatum.value[1]) {
421
+ maxYDatum = datum
422
+ }
423
+ }
424
+ }
425
+ }
426
+
427
+ return {
428
+ datumList,
429
+ minXDatum,
430
+ maxXDatum,
431
+ minYDatum,
432
+ maxYDatum
433
+ }
434
+ })
435
+ )
436
+ }
437
+
438
+ export const multiValueGraphicTransformObservable = ({ minMaxXY$, filteredMinMaxXYData$, fullDataFormatter$, layout$ }: {
439
+ minMaxXY$: Observable<{ minX: number, maxX: number, minY: number, maxY: number }>
440
+ filteredMinMaxXYData$: Observable<{
441
+ minXDatum: ComputedLayoutDatumMultiValue
442
+ maxXDatum: ComputedLayoutDatumMultiValue
443
+ minYDatum: ComputedLayoutDatumMultiValue
444
+ maxYDatum: ComputedLayoutDatumMultiValue
445
+ }>
446
+ fullDataFormatter$: Observable<DataFormatterTypeMap<'multiValue'>>
447
+ layout$: Observable<Layout>
448
+ }): Observable<TransformData> => {
449
+ const destroy$ = new Subject()
450
+
451
+ function calcDataAreaTransform ({ minMaxXY, filteredMinMaxXYData, xAxis, yAxis, width, height }: {
452
+ minMaxXY: { minX: number, maxX: number, minY: number, maxY: number }
453
+ filteredMinMaxXYData: {
454
+ minXDatum: ComputedLayoutDatumMultiValue
455
+ maxXDatum: ComputedLayoutDatumMultiValue
456
+ minYDatum: ComputedLayoutDatumMultiValue
457
+ maxYDatum: ComputedLayoutDatumMultiValue
458
+ }
459
+ xAxis: DataFormatterAxis
460
+ yAxis: DataFormatterAxis
461
+ width: number
462
+ height: number
463
+ }): TransformData {
464
+ // const flatData = data.flat()
465
+
466
+ let translateX = 0
467
+ let translateY = 0
468
+ let scaleX = 0
469
+ let scaleY = 0
470
+
471
+ // // minX, maxX, filteredMinX, filteredMaxX
472
+ // let filteredMinX = 0
473
+ // let filteredMaxX = 0
474
+ // let [minX, maxX] = getMinAndMax(flatData.map(d => d.value[0]))
475
+ // if (minX === maxX) {
476
+ // minX = maxX - 1 // 避免最大及最小值相同造成無法計算scale
477
+ // }
478
+ // if (xAxis.scaleDomain[0] === 'auto' && filteredMinX > 0) {
479
+ // filteredMinX = 0
480
+ // } else if (typeof xAxis.scaleDomain[0] === 'number') {
481
+ // filteredMinX = xAxis.scaleDomain[0] as number
482
+ // } else {
483
+ // filteredMinX = minX
484
+ // }
485
+ // if (xAxis.scaleDomain[1] === 'auto' && filteredMaxX < 0) {
486
+ // filteredMaxX = 0
487
+ // } else if (typeof xAxis.scaleDomain[1] === 'number') {
488
+ // filteredMaxX = xAxis.scaleDomain[1] as number
489
+ // } else {
490
+ // filteredMaxX = maxX
491
+ // }
492
+ // if (filteredMinX === filteredMaxX) {
493
+ // filteredMinX = filteredMaxX - 1 // 避免最大及最小值相同造成無法計算scale
494
+ // }
495
+
496
+ // // minY, maxY, filteredMinY, filteredMaxY
497
+ // let filteredMinY = 0
498
+ // let filteredMaxY = 0
499
+ // let [minY, maxY] = getMinAndMax(flatData.map(d => d.value[1]))
500
+ // console.log('filteredMinMaxXYData', filteredMinMaxXYData)
501
+ let { minX, maxX, minY, maxY } = minMaxXY
502
+ // console.log({ minX, maxX, minY, maxY })
503
+ let filteredMinX = filteredMinMaxXYData.minXDatum.value[0] ?? 0
504
+ let filteredMaxX = filteredMinMaxXYData.maxXDatum.value[0] ?? 0
505
+ let filteredMinY = filteredMinMaxXYData.minYDatum.value[1] ?? 0
506
+ let filteredMaxY = filteredMinMaxXYData.maxYDatum.value[1] ?? 0
507
+
508
+ if (minX === maxX) {
509
+ maxX += 1 // 避免最大及最小值相同造成無法計算scale
510
+ minX -= 1
511
+ }
512
+ if (minY === maxY) {
513
+ maxY += 1 // 避免最大及最小值相同造成無法計算scale
514
+ minY -= 1
515
+ }
516
+ // if (yAxis.scaleDomain[0] === 'auto' && filteredMinY > 0) {
517
+ // filteredMinY = 0
518
+ // } else if (typeof yAxis.scaleDomain[0] === 'number') {
519
+ // filteredMinY = yAxis.scaleDomain[0] as number
520
+ // } else {
521
+ // filteredMinY = minY
522
+ // }
523
+ // if (yAxis.scaleDomain[1] === 'auto' && filteredMaxY < 0) {
524
+ // filteredMaxY = 0
525
+ // } else if (typeof yAxis.scaleDomain[1] === 'number') {
526
+ // filteredMaxY = yAxis.scaleDomain[1] as number
527
+ // } else {
528
+ // filteredMaxY = maxY
529
+ // }
530
+ // if (filteredMinX === filteredMaxX) {
531
+ // filteredMaxX += 1 // 避免最大及最小值相同造成無法計算scale
532
+ // filteredMinX -= 1
533
+ // }
534
+ // if (filteredMinY === filteredMaxY) {
535
+ // filteredMaxY += 1 // 避免最大及最小值相同造成無法計算scale
536
+ // filteredMinY -= 1
537
+ // }
538
+ // console.log({ minX, maxX, minY, maxY, filteredMinX, filteredMaxX, filteredMinY, filteredMaxY })
539
+ // -- xScale --
540
+ const xScale: d3.ScaleLinear<number, number> = createValueToAxisScale({
541
+ maxValue: filteredMaxX,
542
+ minValue: filteredMinX,
543
+ axisWidth: width,
544
+ scaleDomain: xAxis.scaleDomain,
545
+ scaleRange: xAxis.scaleRange
546
+ })
547
+
548
+ // -- translateX, scaleX --
549
+ const rangeMinX = xScale(minX > 0 ? 0 : minX) // * 因為原本的座標就是以 0 到最大值或最小值範範圍計算的,所以這邊也是用同樣的方式計算
550
+ const rangeMaxX = xScale(maxX < 0 ? 0 : maxX) // * 因為原本的座標就是以 0 到最大值或最小值範範圍計算的,所以這邊也是用同樣的方式計算
551
+ translateX = rangeMinX
552
+ const gWidth = rangeMaxX - rangeMinX
553
+ scaleX = gWidth / width
554
+ // console.log({ gWidth, width, rangeMaxX, rangeMinX, scaleX, translateX })
555
+ // -- yScale --
556
+ const yScale: d3.ScaleLinear<number, number> = createValueToAxisScale({
557
+ maxValue: filteredMaxY,
558
+ minValue: filteredMinY,
559
+ axisWidth: height,
560
+ scaleDomain: yAxis.scaleDomain,
561
+ scaleRange: yAxis.scaleRange,
562
+ reverse: true
563
+ })
564
+
565
+ // -- translateY, scaleY --
566
+ const rangeMinY = yScale(minY > 0 ? 0 : minY) // * 因為原本的座標就是以 0 到最大值或最小值範範圍計算的,所以這邊也是用同樣的方式計算
567
+ const rangeMaxY = yScale(maxY < 0 ? 0 : maxY) // * 因為原本的座標就是以 0 到最大值或最小值範範圍計算的,所以這邊也是用同樣的方式計算
568
+ translateY = rangeMaxY // 最大值的 y 最小(最上方)
569
+ const gHeight = rangeMinY - rangeMaxY // 最大的 y 減最小的 y
570
+ scaleY = gHeight / height
571
+ // console.log({ gHeight, height, rangeMaxY, rangeMinY, scaleY, translateY })
572
+
573
+ return {
574
+ translate: [translateX, translateY],
575
+ scale: [scaleX, scaleY],
576
+ rotate: 0,
577
+ rotateX: 0,
578
+ rotateY: 0,
579
+ value: `translate(${translateX}px, ${translateY}px) scale(${scaleX}, ${scaleY})`
580
+ }
581
+ }
582
+
583
+ return new Observable(subscriber => {
584
+ combineLatest({
585
+ minMaxXY: minMaxXY$,
586
+ filteredMinMaxXYData: filteredMinMaxXYData$,
587
+ fullDataFormatter: fullDataFormatter$,
588
+ layout: layout$
589
+ }).pipe(
590
+ takeUntil(destroy$),
591
+ switchMap(async (d) => d),
592
+ ).subscribe(data => {
593
+ const dataAreaTransformData = calcDataAreaTransform({
594
+ minMaxXY: data.minMaxXY,
595
+ filteredMinMaxXYData: data.filteredMinMaxXYData,
596
+ xAxis: data.fullDataFormatter.xAxis,
597
+ yAxis: data.fullDataFormatter.yAxis,
598
+ width: data.layout.width,
599
+ height: data.layout.height
600
+ })
601
+
602
+ // console.log('dataAreaTransformData', dataAreaTransformData)
603
+
604
+ subscriber.next(dataAreaTransformData)
605
+ })
606
+
607
+ return function unscbscribe () {
608
+ destroy$.next(undefined)
609
+ }
610
+ })
611
+ }
612
+
613
+ export const multiValueGraphicReverseScaleObservable = ({ multiValueContainerPosition$, multiValueGraphicTransform$ }: {
614
+ multiValueContainerPosition$: Observable<ContainerPositionScaled[]>
615
+ // multiValueAxesTransform$: Observable<TransformData>
616
+ multiValueGraphicTransform$: Observable<TransformData>
617
+ }): Observable<[number, number][]> => {
618
+ return combineLatest({
619
+ multiValueContainerPosition: multiValueContainerPosition$,
620
+ // multiValueAxesTransform: multiValueAxesTransform$,
621
+ multiValueGraphicTransform: multiValueGraphicTransform$,
622
+ }).pipe(
623
+ switchMap(async (d) => d),
624
+ map(data => {
625
+ // if (data.multiValueAxesTransform.rotate == 0 || data.multiValueAxesTransform.rotate == 180) {
626
+ return data.multiValueContainerPosition.map((series, seriesIndex) => {
627
+ return [
628
+ 1 / data.multiValueGraphicTransform.scale[0] / data.multiValueContainerPosition[seriesIndex].scale[0],
629
+ 1 / data.multiValueGraphicTransform.scale[1] / data.multiValueContainerPosition[seriesIndex].scale[1],
630
+ ]
631
+ })
632
+ // } else {
633
+ // return data.multiValueContainerPosition.map((series, seriesIndex) => {
634
+ // // 由於有垂直的旋轉,所以外層 (container) x和y的scale要互換
635
+ // return [
636
+ // 1 / data.multiValueGraphicTransform.scale[0] / data.multiValueContainerPosition[seriesIndex].scale[1],
637
+ // 1 / data.multiValueGraphicTransform.scale[1] / data.multiValueContainerPosition[seriesIndex].scale[0],
638
+ // ]
639
+ // })
640
+ // }
641
+ }),
642
+ )
643
+ }