@orbcharts/core 3.0.0-alpha.39 → 3.0.0-alpha.40

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. package/LICENSE +200 -200
  2. package/dist/orbcharts-core.es.js +6 -6
  3. package/dist/orbcharts-core.umd.js +1 -1
  4. package/dist/src/types/ContextObserverGrid.d.ts +1 -1
  5. package/dist/src/types/ContextObserverSeries.d.ts +1 -1
  6. package/dist/src/types/ContextObserverTree.d.ts +1 -1
  7. package/dist/src/utils/observables.d.ts +5 -5
  8. package/package.json +41 -41
  9. package/src/AbstractChart.ts +48 -48
  10. package/src/GridChart.ts +20 -20
  11. package/src/MultiGridChart.ts +20 -20
  12. package/src/MultiValueChart.ts +20 -20
  13. package/src/RelationshipChart.ts +20 -20
  14. package/src/SeriesChart.ts +20 -20
  15. package/src/TreeChart.ts +20 -20
  16. package/src/base/createBaseChart.ts +367 -367
  17. package/src/base/createBasePlugin.ts +89 -89
  18. package/src/defaults.ts +247 -247
  19. package/src/defineGridPlugin.ts +3 -3
  20. package/src/defineMultiGridPlugin.ts +3 -3
  21. package/src/defineMultiValuePlugin.ts +3 -3
  22. package/src/defineNoneDataPlugin.ts +4 -4
  23. package/src/defineRelationshipPlugin.ts +3 -3
  24. package/src/defineSeriesPlugin.ts +3 -3
  25. package/src/defineTreePlugin.ts +3 -3
  26. package/src/grid/computeGridData.ts +205 -205
  27. package/src/grid/createGridContextObserver.ts +124 -124
  28. package/src/grid/gridObservables.ts +486 -486
  29. package/src/index.ts +21 -21
  30. package/src/multiGrid/computeMultiGridData.ts +173 -173
  31. package/src/multiGrid/createMultiGridContextObserver.ts +34 -34
  32. package/src/multiGrid/multiGridObservables.ts +285 -285
  33. package/src/multiValue/computeMultiValueData.ts +136 -136
  34. package/src/multiValue/createMultiValueContextObserver.ts +12 -12
  35. package/src/relationship/computeRelationshipData.ts +106 -106
  36. package/src/relationship/createRelationshipContextObserver.ts +12 -12
  37. package/src/series/computeSeriesData.ts +153 -153
  38. package/src/series/createSeriesContextObserver.ts +33 -33
  39. package/src/series/seriesObservables.ts +23 -23
  40. package/src/tree/computeTreeData.ts +128 -128
  41. package/src/tree/createTreeContextObserver.ts +56 -56
  42. package/src/tree/treeObservables.ts +94 -94
  43. package/src/types/Chart.ts +48 -48
  44. package/src/types/ChartParams.ts +51 -51
  45. package/src/types/ComputedData.ts +82 -82
  46. package/src/types/ComputedDataGrid.ts +13 -13
  47. package/src/types/ComputedDataMultiGrid.ts +2 -2
  48. package/src/types/ComputedDataMultiValue.ts +9 -9
  49. package/src/types/ComputedDataRelationship.ts +19 -19
  50. package/src/types/ComputedDataSeries.ts +7 -7
  51. package/src/types/ComputedDataTree.ts +19 -19
  52. package/src/types/ContextObserver.ts +38 -38
  53. package/src/types/ContextObserverGrid.ts +33 -33
  54. package/src/types/ContextObserverMultiGrid.ts +27 -27
  55. package/src/types/ContextObserverMultiValue.ts +4 -4
  56. package/src/types/ContextObserverRelationship.ts +4 -4
  57. package/src/types/ContextObserverSeries.ts +7 -7
  58. package/src/types/ContextObserverTree.ts +10 -10
  59. package/src/types/ContextSubject.ts +18 -18
  60. package/src/types/Data.ts +45 -45
  61. package/src/types/DataFormatter.ts +95 -95
  62. package/src/types/DataFormatterGrid.ts +55 -55
  63. package/src/types/DataFormatterMultiGrid.ts +42 -42
  64. package/src/types/DataFormatterMultiValue.ts +20 -20
  65. package/src/types/DataFormatterRelationship.ts +22 -22
  66. package/src/types/DataFormatterSeries.ts +29 -29
  67. package/src/types/DataFormatterTree.ts +12 -12
  68. package/src/types/DataGrid.ts +11 -11
  69. package/src/types/DataMultiGrid.ts +6 -6
  70. package/src/types/DataMultiValue.ts +12 -12
  71. package/src/types/DataRelationship.ts +27 -27
  72. package/src/types/DataSeries.ts +11 -11
  73. package/src/types/DataTree.ts +20 -20
  74. package/src/types/Event.ts +153 -153
  75. package/src/types/Layout.ts +11 -11
  76. package/src/types/Padding.ts +5 -5
  77. package/src/types/Plugin.ts +60 -60
  78. package/src/types/TransformData.ts +7 -7
  79. package/src/types/index.ts +37 -37
  80. package/src/utils/commonUtils.ts +50 -50
  81. package/src/utils/d3Utils.ts +89 -89
  82. package/src/utils/index.ts +4 -4
  83. package/src/utils/observables.ts +181 -183
  84. package/src/utils/orbchartsUtils.ts +253 -253
  85. package/tsconfig.json +13 -13
  86. package/vite.config.js +44 -44
@@ -1,487 +1,487 @@
1
- import {
2
- combineLatest,
3
- distinctUntilChanged,
4
- filter,
5
- map,
6
- merge,
7
- takeUntil,
8
- shareReplay,
9
- switchMap,
10
- Subject,
11
- Observable } from 'rxjs'
12
- import type {
13
- AxisPosition,
14
- ChartType,
15
- ChartParams,
16
- ComputedDataTypeMap,
17
- ComputedDatumTypeMap,
18
- ContextObserverFn,
19
- DataTypeMap,
20
- DataFormatterTypeMap,
21
- DataFormatterGrid,
22
- DataFormatterGridContainer,
23
- DataFormatterValueAxis,
24
- DataFormatterGroupAxis,
25
- ContainerPosition,
26
- HighlightTarget,
27
- Layout,
28
- TransformData } from '../types'
29
- import { getMinAndMaxGrid } from '../utils/orbchartsUtils'
30
- import { createAxisLinearScale, createAxisPointScale, createAxisQuantizeScale } from '../utils/d3Utils'
31
- import { highlightObservable } from '../utils/observables'
32
- import { calcGridContainerPosition } from '../utils/orbchartsUtils'
33
- import { DATA_FORMATTER_GRID_GRID_DEFAULT } from '../defaults'
34
-
35
- export const gridAxesTransformObservable = ({ fullDataFormatter$, layout$ }: {
36
- fullDataFormatter$: Observable<DataFormatterTypeMap<'grid'>>
37
- layout$: Observable<Layout>
38
- }): Observable<TransformData> => {
39
- const destroy$ = new Subject()
40
-
41
- function calcAxesTransform ({ xAxis, yAxis, width, height }: {
42
- xAxis: DataFormatterGroupAxis | DataFormatterValueAxis,
43
- yAxis: DataFormatterValueAxis,
44
- width: number,
45
- height: number
46
- }): TransformData {
47
- if (!xAxis || !yAxis) {
48
- return {
49
- translate: [0, 0],
50
- scale: [1, 1],
51
- rotate: 0,
52
- rotateX: 0,
53
- rotateY: 0,
54
- value: ''
55
- }
56
- }
57
- // const width = size.width - fullChartParams.layout.left - fullChartParams.layout.right
58
- // const height = size.height - fullChartParams.layout.top - fullChartParams.layout.bottom
59
- let translateX = 0
60
- let translateY = 0
61
- let rotate = 0
62
- let rotateX = 0
63
- let rotateY = 0
64
- if (xAxis.position === 'bottom') {
65
- if (yAxis.position === 'left') {
66
- rotateX = 180
67
- translateY = height
68
- } else if (yAxis.position === 'right') {
69
- rotateX = 180
70
- rotateY = 180
71
- translateX = width
72
- translateY = height
73
- } else {
74
- // 預設
75
- rotateX = 180
76
- translateY = height
77
- }
78
- } else if (xAxis.position === 'top') {
79
- if (yAxis.position === 'left') {
80
- } else if (yAxis.position === 'right') {
81
- rotateY = 180
82
- translateX = width
83
- } else {
84
- // 預設
85
- rotateX = 180
86
- translateY = height
87
- }
88
- } else if (xAxis.position === 'left') {
89
- if (yAxis.position === 'bottom') {
90
- rotate = -90
91
- translateY = height
92
- } else if (yAxis.position === 'top') {
93
- rotate = -90
94
- rotateY = 180
95
- } else {
96
- // 預設
97
- rotateX = 180
98
- translateY = height
99
- }
100
- } else if (xAxis.position === 'right') {
101
- if (yAxis.position === 'bottom') {
102
- rotate = -90
103
- rotateX = 180
104
- translateY = height
105
- translateX = width
106
- } else if (yAxis.position === 'top') {
107
- rotate = -90
108
- rotateX = 180
109
- rotateY = 180
110
- translateX = width
111
- } else {
112
- // 預設
113
- rotateX = 180
114
- translateY = height
115
- }
116
- }
117
- // selection.style('transform', `translate(${translateX}px, ${translateY}px) rotate(${rotate}deg) rotateX(${rotateX}deg) rotateY(${rotateY}deg)`)
118
-
119
- return {
120
- translate: [translateX, translateY],
121
- scale: [1, 1],
122
- rotate,
123
- rotateX,
124
- rotateY,
125
- value: `translate(${translateX}px, ${translateY}px) rotate(${rotate}deg) rotateX(${rotateX}deg) rotateY(${rotateY}deg)`
126
- }
127
- }
128
-
129
- return new Observable(subscriber => {
130
- combineLatest({
131
- fullDataFormatter: fullDataFormatter$,
132
- layout: layout$
133
- }).pipe(
134
- takeUntil(destroy$),
135
- switchMap(async (d) => d),
136
- ).subscribe(data => {
137
- const axesTransformData = calcAxesTransform({
138
- xAxis: data.fullDataFormatter.grid.groupAxis,
139
- yAxis: data.fullDataFormatter.grid.valueAxis,
140
- width: data.layout.width,
141
- height: data.layout.height
142
- })
143
-
144
- subscriber.next(axesTransformData)
145
- })
146
-
147
- return function unscbscribe () {
148
- destroy$.next(undefined)
149
- }
150
- })
151
- }
152
-
153
-
154
- export const gridAxesReverseTransformObservable = ({ gridAxesTransform$ }: {
155
- gridAxesTransform$: Observable<TransformData>
156
- }): Observable<TransformData> => {
157
- return gridAxesTransform$.pipe(
158
- map(d => {
159
- // const translate: [number, number] = [d.translate[0] * -1, d.translate[1] * -1]
160
- const translate: [number, number] = [0, 0] // 無需逆轉
161
- const scale: [number, number] = [1 / d.scale[0], 1 / d.scale[1]]
162
- const rotate = d.rotate * -1
163
- const rotateX = d.rotateX * -1
164
- const rotateY = d.rotateY * -1
165
- return {
166
- translate,
167
- scale,
168
- rotate,
169
- rotateX,
170
- rotateY,
171
- value: `translate(${translate[0]}px, ${translate[1]}px) rotate(${rotate}deg) rotateX(${rotateX}deg) rotateY(${rotateY}deg)`
172
- }
173
- }),
174
- )
175
- }
176
-
177
- export const gridGraphicTransformObservable = ({ computedData$, fullDataFormatter$, layout$ }: {
178
- computedData$: Observable<ComputedDataTypeMap<'grid'>>
179
- fullDataFormatter$: Observable<DataFormatterTypeMap<'grid'>>
180
- layout$: Observable<Layout>
181
- }): Observable<TransformData> => {
182
- const destroy$ = new Subject()
183
-
184
- function calcGridDataAreaTransform ({ data, groupAxis, valueAxis, width, height }: {
185
- data: ComputedDataTypeMap<'grid'>
186
- groupAxis: DataFormatterGroupAxis
187
- valueAxis: DataFormatterValueAxis
188
- width: number
189
- height: number
190
- }): TransformData {
191
- let translateX = 0
192
- let translateY = 0
193
- let scaleX = 0
194
- let scaleY = 0
195
-
196
- // -- groupScale --
197
- const groupAxisWidth = (groupAxis.position === 'top' || groupAxis.position === 'bottom')
198
- ? width
199
- : height
200
- const groupMin = 0
201
- const groupMax = data[0] ? data[0].length - 1 : 0
202
- const groupScaleDomainMin = groupAxis.scaleDomain[0] === 'auto'
203
- ? groupMin - groupAxis.scalePadding
204
- : groupAxis.scaleDomain[0] as number - groupAxis.scalePadding
205
- const groupScaleDomainMax = groupAxis.scaleDomain[1] === 'auto'
206
- ? groupMax + groupAxis.scalePadding
207
- : groupAxis.scaleDomain[1] as number + groupAxis.scalePadding
208
-
209
- const groupScale: d3.ScaleLinear<number, number> = createAxisLinearScale({
210
- maxValue: groupMax,
211
- minValue: groupMin,
212
- axisWidth: groupAxisWidth,
213
- // scaleDomain: groupAxis.scaleDomain,
214
- scaleDomain: [groupScaleDomainMin, groupScaleDomainMax],
215
- scaleRange: [0, 1]
216
- })
217
-
218
- // -- translateX, scaleX --
219
- const rangeMinX = groupScale(groupMin)
220
- const rangeMaxX = groupScale(groupMax)
221
- translateX = rangeMinX
222
- const gWidth = rangeMaxX - rangeMinX
223
- scaleX = gWidth / groupAxisWidth
224
-
225
- // -- valueScale --
226
- const filteredData = data.map((d, i) => {
227
- return d.filter((_d, _i) => {
228
- return _i >= groupScaleDomainMin && _i <= groupScaleDomainMax && _d.visible == true
229
- })
230
- })
231
-
232
- const filteredMinAndMax = getMinAndMaxGrid(filteredData)
233
-
234
- const valueAxisWidth = (valueAxis.position === 'left' || valueAxis.position === 'right')
235
- ? height
236
- : width
237
-
238
- const valueScale: d3.ScaleLinear<number, number> = createAxisLinearScale({
239
- maxValue: filteredMinAndMax[1],
240
- minValue: filteredMinAndMax[0],
241
- axisWidth: valueAxisWidth,
242
- scaleDomain: valueAxis.scaleDomain,
243
- scaleRange: valueAxis.scaleRange
244
- })
245
-
246
- // -- translateY, scaleY --
247
- const minAndMax = getMinAndMaxGrid(data)
248
- const rangeMinY = valueScale(minAndMax[0])
249
- const rangeMaxY = valueScale(minAndMax[1])
250
- translateY = rangeMinY
251
- const gHeight = rangeMaxY - rangeMinY
252
- scaleY = gHeight / valueAxisWidth
253
-
254
- return {
255
- translate: [translateX, translateY],
256
- scale: [scaleX, scaleY],
257
- rotate: 0,
258
- rotateX: 0,
259
- rotateY: 0,
260
- value: `translate(${translateX}px, ${translateY}px) scale(${scaleX}, ${scaleY})`
261
- }
262
- }
263
-
264
- return new Observable(subscriber => {
265
- combineLatest({
266
- computedData: computedData$,
267
- fullDataFormatter: fullDataFormatter$,
268
- layout: layout$
269
- }).pipe(
270
- takeUntil(destroy$),
271
- switchMap(async (d) => d),
272
- ).subscribe(data => {
273
- const dataAreaTransformData = calcGridDataAreaTransform ({
274
- data: data.computedData,
275
- groupAxis: data.fullDataFormatter.grid.groupAxis,
276
- valueAxis: data.fullDataFormatter.grid.valueAxis,
277
- width: data.layout.width,
278
- height: data.layout.height
279
- })
280
-
281
- subscriber.next(dataAreaTransformData)
282
- })
283
-
284
- return function unscbscribe () {
285
- destroy$.next(undefined)
286
- }
287
- })
288
- }
289
-
290
- export const gridGraphicReverseScaleObservable = ({ gridContainer$, gridAxesTransform$, gridGraphicTransform$ }: {
291
- gridContainer$: Observable<ContainerPosition[]>
292
- gridAxesTransform$: Observable<TransformData>
293
- gridGraphicTransform$: Observable<TransformData>
294
- }): Observable<[number, number][]> => {
295
- return combineLatest({
296
- gridContainer: gridContainer$,
297
- gridAxesTransform: gridAxesTransform$,
298
- gridGraphicTransform: gridGraphicTransform$,
299
- }).pipe(
300
- switchMap(async (d) => d),
301
- map(data => {
302
- if (data.gridAxesTransform.rotate == 0 || data.gridAxesTransform.rotate == 180) {
303
- return data.gridContainer.map((series, seriesIndex) => {
304
- return [
305
- 1 / data.gridGraphicTransform.scale[0] / data.gridContainer[seriesIndex].scale[0],
306
- 1 / data.gridGraphicTransform.scale[1] / data.gridContainer[seriesIndex].scale[1],
307
- ]
308
- })
309
- } else {
310
- return data.gridContainer.map((series, seriesIndex) => {
311
- // 由於有垂直的旋轉,所以外層 (container) x和y的scale要互換
312
- return [
313
- 1 / data.gridGraphicTransform.scale[0] / data.gridContainer[seriesIndex].scale[1],
314
- 1 / data.gridGraphicTransform.scale[1] / data.gridContainer[seriesIndex].scale[0],
315
- ]
316
- })
317
- }
318
- }),
319
- )
320
- }
321
-
322
- export const gridAxesSizeObservable = ({ fullDataFormatter$, layout$ }: {
323
- fullDataFormatter$: Observable<DataFormatterGrid>
324
- layout$: Observable<Layout>
325
- }): Observable<{
326
- width: number;
327
- height: number;
328
- }> => {
329
- const destroy$ = new Subject()
330
-
331
- function calcAxesSize ({ xAxisPosition, yAxisPosition, width, height }: {
332
- xAxisPosition: AxisPosition
333
- yAxisPosition: AxisPosition
334
- width: number
335
- height: number
336
- }) {
337
- if ((xAxisPosition === 'bottom' || xAxisPosition === 'top') && (yAxisPosition === 'left' || yAxisPosition === 'right')) {
338
- return { width, height }
339
- } else if ((xAxisPosition === 'left' || xAxisPosition === 'right') && (yAxisPosition === 'bottom' || yAxisPosition === 'top')) {
340
- return {
341
- width: height,
342
- height: width
343
- }
344
- }
345
- }
346
-
347
- return new Observable(subscriber => {
348
- combineLatest({
349
- fullDataFormatter: fullDataFormatter$,
350
- layout: layout$
351
- }).pipe(
352
- takeUntil(destroy$),
353
- switchMap(async (d) => d),
354
- ).subscribe(data => {
355
-
356
- const axisSize = calcAxesSize({
357
- xAxisPosition: data.fullDataFormatter.grid.groupAxis.position,
358
- yAxisPosition: data.fullDataFormatter.grid.valueAxis.position,
359
- width: data.layout.width,
360
- height: data.layout.height,
361
- })
362
-
363
- subscriber.next(axisSize)
364
-
365
- return function unsubscribe () {
366
- destroy$.next(undefined)
367
- }
368
- })
369
- })
370
- }
371
-
372
- // export const gridHighlightObservable = ({ computedData$, fullChartParams$, event$ }: {
373
- // computedData$: Observable<ComputedDataTypeMap<'grid'>>
374
- // fullChartParams$: Observable<ChartParams>
375
- // event$: Subject<any>
376
- // }): Observable<string[]> => {
377
- // const datumList$ = computedData$.pipe(
378
- // map(d => d.flat())
379
- // )
380
- // return highlightObservable ({ datumList$, fullChartParams$, event$ })
381
- // }
382
-
383
- export const existSeriesLabelsObservable = ({ computedData$ }: { computedData$: Observable<ComputedDataTypeMap<'grid'>> }) => {
384
- return computedData$.pipe(
385
- map(data => {
386
- return data
387
- .filter(series => series.length)
388
- .map(series => {
389
- return series[0].seriesLabel
390
- })
391
- }),
392
- distinctUntilChanged((a, b) => {
393
- return JSON.stringify(a).length === JSON.stringify(b).length
394
- }),
395
- )
396
- }
397
-
398
- export const gridVisibleComputedDataObservable = ({ computedData$ }: { computedData$: Observable<ComputedDataTypeMap<'grid'>> }) => {
399
- return computedData$.pipe(
400
- map(data => {
401
- const visibleComputedData = data
402
- .map(d => {
403
- return d.filter(_d => {
404
- return _d.visible == true
405
- })
406
- })
407
- .filter(d => d.length)
408
- return visibleComputedData
409
- })
410
- )
411
- }
412
-
413
- export const isSeriesPositionSeprateObservable = ({ computedData$, fullDataFormatter$ }: {
414
- computedData$: Observable<ComputedDataTypeMap<'grid'>>
415
- fullDataFormatter$: Observable<DataFormatterTypeMap<'grid'>>
416
- }) => {
417
- return combineLatest({
418
- computedData: computedData$,
419
- fullDataFormatter: fullDataFormatter$
420
- }).pipe(
421
- map(data => {
422
- return data.fullDataFormatter.grid.seriesSlotIndexes && data.fullDataFormatter.grid.seriesSlotIndexes.length === data.computedData.length
423
- ? true
424
- : false
425
- }),
426
- distinctUntilChanged()
427
- )
428
- }
429
-
430
- // 所有container位置(對應series)
431
- export const gridContainerObservable = ({ computedData$, fullDataFormatter$, fullChartParams$, layout$ }: {
432
- computedData$: Observable<ComputedDataTypeMap<'grid'>>
433
- fullDataFormatter$: Observable<DataFormatterTypeMap<'grid'>>
434
- fullChartParams$: Observable<ChartParams>
435
- layout$: Observable<Layout>
436
- }) => {
437
-
438
- const gridContainer$ = combineLatest({
439
- computedData: computedData$,
440
- fullDataFormatter: fullDataFormatter$,
441
- fullChartParams: fullChartParams$,
442
- layout: layout$,
443
- }).pipe(
444
- switchMap(async (d) => d),
445
- map(data => {
446
-
447
- const grid = data.fullDataFormatter.grid
448
-
449
- // 有設定series定位
450
- const hasSeriesPosition = grid.seriesSlotIndexes && grid.seriesSlotIndexes.length === data.computedData.length
451
- ? true
452
- : false
453
-
454
- if (hasSeriesPosition) {
455
- // -- 依seriesSlotIndexes計算 --
456
- return data.computedData.map((seriesData, seriesIndex) => {
457
- const columnIndex = grid.seriesSlotIndexes[seriesIndex] % data.fullDataFormatter.container.columnAmount
458
- const rowIndex = Math.floor(grid.seriesSlotIndexes[seriesIndex] / data.fullDataFormatter.container.columnAmount)
459
- const { translate, scale } = calcGridContainerPosition(data.layout, data.fullDataFormatter.container, rowIndex, columnIndex)
460
- return {
461
- slotIndex: grid.seriesSlotIndexes[seriesIndex],
462
- rowIndex,
463
- columnIndex,
464
- translate,
465
- scale,
466
- }
467
- })
468
- } else {
469
- // -- 依grid的slotIndex計算 --
470
- const columnIndex = grid.slotIndex % data.fullDataFormatter.container.columnAmount
471
- const rowIndex = Math.floor(grid.slotIndex / data.fullDataFormatter.container.columnAmount)
472
- return data.computedData.map((seriesData, seriesIndex) => {
473
- const { translate, scale } = calcGridContainerPosition(data.layout, data.fullDataFormatter.container, rowIndex, columnIndex)
474
- return {
475
- slotIndex: grid.slotIndex,
476
- rowIndex,
477
- columnIndex,
478
- translate,
479
- scale,
480
- }
481
- })
482
- }
483
- })
484
- )
485
-
486
- return gridContainer$
1
+ import {
2
+ combineLatest,
3
+ distinctUntilChanged,
4
+ filter,
5
+ map,
6
+ merge,
7
+ takeUntil,
8
+ shareReplay,
9
+ switchMap,
10
+ Subject,
11
+ Observable } from 'rxjs'
12
+ import type {
13
+ AxisPosition,
14
+ ChartType,
15
+ ChartParams,
16
+ ComputedDataTypeMap,
17
+ ComputedDatumTypeMap,
18
+ ContextObserverFn,
19
+ DataTypeMap,
20
+ DataFormatterTypeMap,
21
+ DataFormatterGrid,
22
+ DataFormatterGridContainer,
23
+ DataFormatterValueAxis,
24
+ DataFormatterGroupAxis,
25
+ ContainerPosition,
26
+ HighlightTarget,
27
+ Layout,
28
+ TransformData } from '../types'
29
+ import { getMinAndMaxGrid } from '../utils/orbchartsUtils'
30
+ import { createAxisLinearScale, createAxisPointScale, createAxisQuantizeScale } from '../utils/d3Utils'
31
+ import { highlightObservable } from '../utils/observables'
32
+ import { calcGridContainerPosition } from '../utils/orbchartsUtils'
33
+ import { DATA_FORMATTER_GRID_GRID_DEFAULT } from '../defaults'
34
+
35
+ export const gridAxesTransformObservable = ({ fullDataFormatter$, layout$ }: {
36
+ fullDataFormatter$: Observable<DataFormatterTypeMap<'grid'>>
37
+ layout$: Observable<Layout>
38
+ }): Observable<TransformData> => {
39
+ const destroy$ = new Subject()
40
+
41
+ function calcAxesTransform ({ xAxis, yAxis, width, height }: {
42
+ xAxis: DataFormatterGroupAxis | DataFormatterValueAxis,
43
+ yAxis: DataFormatterValueAxis,
44
+ width: number,
45
+ height: number
46
+ }): TransformData {
47
+ if (!xAxis || !yAxis) {
48
+ return {
49
+ translate: [0, 0],
50
+ scale: [1, 1],
51
+ rotate: 0,
52
+ rotateX: 0,
53
+ rotateY: 0,
54
+ value: ''
55
+ }
56
+ }
57
+ // const width = size.width - fullChartParams.layout.left - fullChartParams.layout.right
58
+ // const height = size.height - fullChartParams.layout.top - fullChartParams.layout.bottom
59
+ let translateX = 0
60
+ let translateY = 0
61
+ let rotate = 0
62
+ let rotateX = 0
63
+ let rotateY = 0
64
+ if (xAxis.position === 'bottom') {
65
+ if (yAxis.position === 'left') {
66
+ rotateX = 180
67
+ translateY = height
68
+ } else if (yAxis.position === 'right') {
69
+ rotateX = 180
70
+ rotateY = 180
71
+ translateX = width
72
+ translateY = height
73
+ } else {
74
+ // 預設
75
+ rotateX = 180
76
+ translateY = height
77
+ }
78
+ } else if (xAxis.position === 'top') {
79
+ if (yAxis.position === 'left') {
80
+ } else if (yAxis.position === 'right') {
81
+ rotateY = 180
82
+ translateX = width
83
+ } else {
84
+ // 預設
85
+ rotateX = 180
86
+ translateY = height
87
+ }
88
+ } else if (xAxis.position === 'left') {
89
+ if (yAxis.position === 'bottom') {
90
+ rotate = -90
91
+ translateY = height
92
+ } else if (yAxis.position === 'top') {
93
+ rotate = -90
94
+ rotateY = 180
95
+ } else {
96
+ // 預設
97
+ rotateX = 180
98
+ translateY = height
99
+ }
100
+ } else if (xAxis.position === 'right') {
101
+ if (yAxis.position === 'bottom') {
102
+ rotate = -90
103
+ rotateX = 180
104
+ translateY = height
105
+ translateX = width
106
+ } else if (yAxis.position === 'top') {
107
+ rotate = -90
108
+ rotateX = 180
109
+ rotateY = 180
110
+ translateX = width
111
+ } else {
112
+ // 預設
113
+ rotateX = 180
114
+ translateY = height
115
+ }
116
+ }
117
+ // selection.style('transform', `translate(${translateX}px, ${translateY}px) rotate(${rotate}deg) rotateX(${rotateX}deg) rotateY(${rotateY}deg)`)
118
+
119
+ return {
120
+ translate: [translateX, translateY],
121
+ scale: [1, 1],
122
+ rotate,
123
+ rotateX,
124
+ rotateY,
125
+ value: `translate(${translateX}px, ${translateY}px) rotate(${rotate}deg) rotateX(${rotateX}deg) rotateY(${rotateY}deg)`
126
+ }
127
+ }
128
+
129
+ return new Observable(subscriber => {
130
+ combineLatest({
131
+ fullDataFormatter: fullDataFormatter$,
132
+ layout: layout$
133
+ }).pipe(
134
+ takeUntil(destroy$),
135
+ switchMap(async (d) => d),
136
+ ).subscribe(data => {
137
+ const axesTransformData = calcAxesTransform({
138
+ xAxis: data.fullDataFormatter.grid.groupAxis,
139
+ yAxis: data.fullDataFormatter.grid.valueAxis,
140
+ width: data.layout.width,
141
+ height: data.layout.height
142
+ })
143
+
144
+ subscriber.next(axesTransformData)
145
+ })
146
+
147
+ return function unscbscribe () {
148
+ destroy$.next(undefined)
149
+ }
150
+ })
151
+ }
152
+
153
+
154
+ export const gridAxesReverseTransformObservable = ({ gridAxesTransform$ }: {
155
+ gridAxesTransform$: Observable<TransformData>
156
+ }): Observable<TransformData> => {
157
+ return gridAxesTransform$.pipe(
158
+ map(d => {
159
+ // const translate: [number, number] = [d.translate[0] * -1, d.translate[1] * -1]
160
+ const translate: [number, number] = [0, 0] // 無需逆轉
161
+ const scale: [number, number] = [1 / d.scale[0], 1 / d.scale[1]]
162
+ const rotate = d.rotate * -1
163
+ const rotateX = d.rotateX * -1
164
+ const rotateY = d.rotateY * -1
165
+ return {
166
+ translate,
167
+ scale,
168
+ rotate,
169
+ rotateX,
170
+ rotateY,
171
+ value: `translate(${translate[0]}px, ${translate[1]}px) rotate(${rotate}deg) rotateX(${rotateX}deg) rotateY(${rotateY}deg)`
172
+ }
173
+ }),
174
+ )
175
+ }
176
+
177
+ export const gridGraphicTransformObservable = ({ computedData$, fullDataFormatter$, layout$ }: {
178
+ computedData$: Observable<ComputedDataTypeMap<'grid'>>
179
+ fullDataFormatter$: Observable<DataFormatterTypeMap<'grid'>>
180
+ layout$: Observable<Layout>
181
+ }): Observable<TransformData> => {
182
+ const destroy$ = new Subject()
183
+
184
+ function calcGridDataAreaTransform ({ data, groupAxis, valueAxis, width, height }: {
185
+ data: ComputedDataTypeMap<'grid'>
186
+ groupAxis: DataFormatterGroupAxis
187
+ valueAxis: DataFormatterValueAxis
188
+ width: number
189
+ height: number
190
+ }): TransformData {
191
+ let translateX = 0
192
+ let translateY = 0
193
+ let scaleX = 0
194
+ let scaleY = 0
195
+
196
+ // -- groupScale --
197
+ const groupAxisWidth = (groupAxis.position === 'top' || groupAxis.position === 'bottom')
198
+ ? width
199
+ : height
200
+ const groupMin = 0
201
+ const groupMax = data[0] ? data[0].length - 1 : 0
202
+ const groupScaleDomainMin = groupAxis.scaleDomain[0] === 'auto'
203
+ ? groupMin - groupAxis.scalePadding
204
+ : groupAxis.scaleDomain[0] as number - groupAxis.scalePadding
205
+ const groupScaleDomainMax = groupAxis.scaleDomain[1] === 'auto'
206
+ ? groupMax + groupAxis.scalePadding
207
+ : groupAxis.scaleDomain[1] as number + groupAxis.scalePadding
208
+
209
+ const groupScale: d3.ScaleLinear<number, number> = createAxisLinearScale({
210
+ maxValue: groupMax,
211
+ minValue: groupMin,
212
+ axisWidth: groupAxisWidth,
213
+ // scaleDomain: groupAxis.scaleDomain,
214
+ scaleDomain: [groupScaleDomainMin, groupScaleDomainMax],
215
+ scaleRange: [0, 1]
216
+ })
217
+
218
+ // -- translateX, scaleX --
219
+ const rangeMinX = groupScale(groupMin)
220
+ const rangeMaxX = groupScale(groupMax)
221
+ translateX = rangeMinX
222
+ const gWidth = rangeMaxX - rangeMinX
223
+ scaleX = gWidth / groupAxisWidth
224
+
225
+ // -- valueScale --
226
+ const filteredData = data.map((d, i) => {
227
+ return d.filter((_d, _i) => {
228
+ return _i >= groupScaleDomainMin && _i <= groupScaleDomainMax && _d.visible == true
229
+ })
230
+ })
231
+
232
+ const filteredMinAndMax = getMinAndMaxGrid(filteredData)
233
+
234
+ const valueAxisWidth = (valueAxis.position === 'left' || valueAxis.position === 'right')
235
+ ? height
236
+ : width
237
+
238
+ const valueScale: d3.ScaleLinear<number, number> = createAxisLinearScale({
239
+ maxValue: filteredMinAndMax[1],
240
+ minValue: filteredMinAndMax[0],
241
+ axisWidth: valueAxisWidth,
242
+ scaleDomain: valueAxis.scaleDomain,
243
+ scaleRange: valueAxis.scaleRange
244
+ })
245
+
246
+ // -- translateY, scaleY --
247
+ const minAndMax = getMinAndMaxGrid(data)
248
+ const rangeMinY = valueScale(minAndMax[0])
249
+ const rangeMaxY = valueScale(minAndMax[1])
250
+ translateY = rangeMinY
251
+ const gHeight = rangeMaxY - rangeMinY
252
+ scaleY = gHeight / valueAxisWidth
253
+
254
+ return {
255
+ translate: [translateX, translateY],
256
+ scale: [scaleX, scaleY],
257
+ rotate: 0,
258
+ rotateX: 0,
259
+ rotateY: 0,
260
+ value: `translate(${translateX}px, ${translateY}px) scale(${scaleX}, ${scaleY})`
261
+ }
262
+ }
263
+
264
+ return new Observable(subscriber => {
265
+ combineLatest({
266
+ computedData: computedData$,
267
+ fullDataFormatter: fullDataFormatter$,
268
+ layout: layout$
269
+ }).pipe(
270
+ takeUntil(destroy$),
271
+ switchMap(async (d) => d),
272
+ ).subscribe(data => {
273
+ const dataAreaTransformData = calcGridDataAreaTransform ({
274
+ data: data.computedData,
275
+ groupAxis: data.fullDataFormatter.grid.groupAxis,
276
+ valueAxis: data.fullDataFormatter.grid.valueAxis,
277
+ width: data.layout.width,
278
+ height: data.layout.height
279
+ })
280
+
281
+ subscriber.next(dataAreaTransformData)
282
+ })
283
+
284
+ return function unscbscribe () {
285
+ destroy$.next(undefined)
286
+ }
287
+ })
288
+ }
289
+
290
+ export const gridGraphicReverseScaleObservable = ({ gridContainer$, gridAxesTransform$, gridGraphicTransform$ }: {
291
+ gridContainer$: Observable<ContainerPosition[]>
292
+ gridAxesTransform$: Observable<TransformData>
293
+ gridGraphicTransform$: Observable<TransformData>
294
+ }): Observable<[number, number][]> => {
295
+ return combineLatest({
296
+ gridContainer: gridContainer$,
297
+ gridAxesTransform: gridAxesTransform$,
298
+ gridGraphicTransform: gridGraphicTransform$,
299
+ }).pipe(
300
+ switchMap(async (d) => d),
301
+ map(data => {
302
+ if (data.gridAxesTransform.rotate == 0 || data.gridAxesTransform.rotate == 180) {
303
+ return data.gridContainer.map((series, seriesIndex) => {
304
+ return [
305
+ 1 / data.gridGraphicTransform.scale[0] / data.gridContainer[seriesIndex].scale[0],
306
+ 1 / data.gridGraphicTransform.scale[1] / data.gridContainer[seriesIndex].scale[1],
307
+ ]
308
+ })
309
+ } else {
310
+ return data.gridContainer.map((series, seriesIndex) => {
311
+ // 由於有垂直的旋轉,所以外層 (container) x和y的scale要互換
312
+ return [
313
+ 1 / data.gridGraphicTransform.scale[0] / data.gridContainer[seriesIndex].scale[1],
314
+ 1 / data.gridGraphicTransform.scale[1] / data.gridContainer[seriesIndex].scale[0],
315
+ ]
316
+ })
317
+ }
318
+ }),
319
+ )
320
+ }
321
+
322
+ export const gridAxesSizeObservable = ({ fullDataFormatter$, layout$ }: {
323
+ fullDataFormatter$: Observable<DataFormatterGrid>
324
+ layout$: Observable<Layout>
325
+ }): Observable<{
326
+ width: number;
327
+ height: number;
328
+ }> => {
329
+ const destroy$ = new Subject()
330
+
331
+ function calcAxesSize ({ xAxisPosition, yAxisPosition, width, height }: {
332
+ xAxisPosition: AxisPosition
333
+ yAxisPosition: AxisPosition
334
+ width: number
335
+ height: number
336
+ }) {
337
+ if ((xAxisPosition === 'bottom' || xAxisPosition === 'top') && (yAxisPosition === 'left' || yAxisPosition === 'right')) {
338
+ return { width, height }
339
+ } else if ((xAxisPosition === 'left' || xAxisPosition === 'right') && (yAxisPosition === 'bottom' || yAxisPosition === 'top')) {
340
+ return {
341
+ width: height,
342
+ height: width
343
+ }
344
+ }
345
+ }
346
+
347
+ return new Observable(subscriber => {
348
+ combineLatest({
349
+ fullDataFormatter: fullDataFormatter$,
350
+ layout: layout$
351
+ }).pipe(
352
+ takeUntil(destroy$),
353
+ switchMap(async (d) => d),
354
+ ).subscribe(data => {
355
+
356
+ const axisSize = calcAxesSize({
357
+ xAxisPosition: data.fullDataFormatter.grid.groupAxis.position,
358
+ yAxisPosition: data.fullDataFormatter.grid.valueAxis.position,
359
+ width: data.layout.width,
360
+ height: data.layout.height,
361
+ })
362
+
363
+ subscriber.next(axisSize)
364
+
365
+ return function unsubscribe () {
366
+ destroy$.next(undefined)
367
+ }
368
+ })
369
+ })
370
+ }
371
+
372
+ // export const gridHighlightObservable = ({ computedData$, fullChartParams$, event$ }: {
373
+ // computedData$: Observable<ComputedDataTypeMap<'grid'>>
374
+ // fullChartParams$: Observable<ChartParams>
375
+ // event$: Subject<any>
376
+ // }): Observable<string[]> => {
377
+ // const datumList$ = computedData$.pipe(
378
+ // map(d => d.flat())
379
+ // )
380
+ // return highlightObservable ({ datumList$, fullChartParams$, event$ })
381
+ // }
382
+
383
+ export const existSeriesLabelsObservable = ({ computedData$ }: { computedData$: Observable<ComputedDataTypeMap<'grid'>> }) => {
384
+ return computedData$.pipe(
385
+ map(data => {
386
+ return data
387
+ .filter(series => series.length)
388
+ .map(series => {
389
+ return series[0].seriesLabel
390
+ })
391
+ }),
392
+ distinctUntilChanged((a, b) => {
393
+ return JSON.stringify(a).length === JSON.stringify(b).length
394
+ }),
395
+ )
396
+ }
397
+
398
+ export const gridVisibleComputedDataObservable = ({ computedData$ }: { computedData$: Observable<ComputedDataTypeMap<'grid'>> }) => {
399
+ return computedData$.pipe(
400
+ map(data => {
401
+ const visibleComputedData = data
402
+ .map(d => {
403
+ return d.filter(_d => {
404
+ return _d.visible == true
405
+ })
406
+ })
407
+ .filter(d => d.length)
408
+ return visibleComputedData
409
+ })
410
+ )
411
+ }
412
+
413
+ export const isSeriesPositionSeprateObservable = ({ computedData$, fullDataFormatter$ }: {
414
+ computedData$: Observable<ComputedDataTypeMap<'grid'>>
415
+ fullDataFormatter$: Observable<DataFormatterTypeMap<'grid'>>
416
+ }) => {
417
+ return combineLatest({
418
+ computedData: computedData$,
419
+ fullDataFormatter: fullDataFormatter$
420
+ }).pipe(
421
+ map(data => {
422
+ return data.fullDataFormatter.grid.seriesSlotIndexes && data.fullDataFormatter.grid.seriesSlotIndexes.length === data.computedData.length
423
+ ? true
424
+ : false
425
+ }),
426
+ distinctUntilChanged()
427
+ )
428
+ }
429
+
430
+ // 所有container位置(對應series)
431
+ export const gridContainerObservable = ({ computedData$, fullDataFormatter$, fullChartParams$, layout$ }: {
432
+ computedData$: Observable<ComputedDataTypeMap<'grid'>>
433
+ fullDataFormatter$: Observable<DataFormatterTypeMap<'grid'>>
434
+ fullChartParams$: Observable<ChartParams>
435
+ layout$: Observable<Layout>
436
+ }) => {
437
+
438
+ const gridContainer$ = combineLatest({
439
+ computedData: computedData$,
440
+ fullDataFormatter: fullDataFormatter$,
441
+ fullChartParams: fullChartParams$,
442
+ layout: layout$,
443
+ }).pipe(
444
+ switchMap(async (d) => d),
445
+ map(data => {
446
+
447
+ const grid = data.fullDataFormatter.grid
448
+
449
+ // 有設定series定位
450
+ const hasSeriesPosition = grid.seriesSlotIndexes && grid.seriesSlotIndexes.length === data.computedData.length
451
+ ? true
452
+ : false
453
+
454
+ if (hasSeriesPosition) {
455
+ // -- 依seriesSlotIndexes計算 --
456
+ return data.computedData.map((seriesData, seriesIndex) => {
457
+ const columnIndex = grid.seriesSlotIndexes[seriesIndex] % data.fullDataFormatter.container.columnAmount
458
+ const rowIndex = Math.floor(grid.seriesSlotIndexes[seriesIndex] / data.fullDataFormatter.container.columnAmount)
459
+ const { translate, scale } = calcGridContainerPosition(data.layout, data.fullDataFormatter.container, rowIndex, columnIndex)
460
+ return {
461
+ slotIndex: grid.seriesSlotIndexes[seriesIndex],
462
+ rowIndex,
463
+ columnIndex,
464
+ translate,
465
+ scale,
466
+ }
467
+ })
468
+ } else {
469
+ // -- 依grid的slotIndex計算 --
470
+ const columnIndex = grid.slotIndex % data.fullDataFormatter.container.columnAmount
471
+ const rowIndex = Math.floor(grid.slotIndex / data.fullDataFormatter.container.columnAmount)
472
+ return data.computedData.map((seriesData, seriesIndex) => {
473
+ const { translate, scale } = calcGridContainerPosition(data.layout, data.fullDataFormatter.container, rowIndex, columnIndex)
474
+ return {
475
+ slotIndex: grid.slotIndex,
476
+ rowIndex,
477
+ columnIndex,
478
+ translate,
479
+ scale,
480
+ }
481
+ })
482
+ }
483
+ })
484
+ )
485
+
486
+ return gridContainer$
487
487
  }