@orbcharts/core 3.0.0-alpha.39 → 3.0.0-alpha.40
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.
- package/LICENSE +200 -200
- package/dist/orbcharts-core.es.js +6 -6
- package/dist/orbcharts-core.umd.js +1 -1
- package/dist/src/types/ContextObserverGrid.d.ts +1 -1
- package/dist/src/types/ContextObserverSeries.d.ts +1 -1
- package/dist/src/types/ContextObserverTree.d.ts +1 -1
- package/dist/src/utils/observables.d.ts +5 -5
- package/package.json +41 -41
- package/src/AbstractChart.ts +48 -48
- package/src/GridChart.ts +20 -20
- package/src/MultiGridChart.ts +20 -20
- package/src/MultiValueChart.ts +20 -20
- package/src/RelationshipChart.ts +20 -20
- package/src/SeriesChart.ts +20 -20
- package/src/TreeChart.ts +20 -20
- package/src/base/createBaseChart.ts +367 -367
- package/src/base/createBasePlugin.ts +89 -89
- package/src/defaults.ts +247 -247
- package/src/defineGridPlugin.ts +3 -3
- package/src/defineMultiGridPlugin.ts +3 -3
- package/src/defineMultiValuePlugin.ts +3 -3
- package/src/defineNoneDataPlugin.ts +4 -4
- package/src/defineRelationshipPlugin.ts +3 -3
- package/src/defineSeriesPlugin.ts +3 -3
- package/src/defineTreePlugin.ts +3 -3
- package/src/grid/computeGridData.ts +205 -205
- package/src/grid/createGridContextObserver.ts +124 -124
- package/src/grid/gridObservables.ts +486 -486
- package/src/index.ts +21 -21
- package/src/multiGrid/computeMultiGridData.ts +173 -173
- package/src/multiGrid/createMultiGridContextObserver.ts +34 -34
- package/src/multiGrid/multiGridObservables.ts +285 -285
- package/src/multiValue/computeMultiValueData.ts +136 -136
- package/src/multiValue/createMultiValueContextObserver.ts +12 -12
- package/src/relationship/computeRelationshipData.ts +106 -106
- package/src/relationship/createRelationshipContextObserver.ts +12 -12
- package/src/series/computeSeriesData.ts +153 -153
- package/src/series/createSeriesContextObserver.ts +33 -33
- package/src/series/seriesObservables.ts +23 -23
- package/src/tree/computeTreeData.ts +128 -128
- package/src/tree/createTreeContextObserver.ts +56 -56
- package/src/tree/treeObservables.ts +94 -94
- package/src/types/Chart.ts +48 -48
- package/src/types/ChartParams.ts +51 -51
- package/src/types/ComputedData.ts +82 -82
- package/src/types/ComputedDataGrid.ts +13 -13
- package/src/types/ComputedDataMultiGrid.ts +2 -2
- package/src/types/ComputedDataMultiValue.ts +9 -9
- package/src/types/ComputedDataRelationship.ts +19 -19
- package/src/types/ComputedDataSeries.ts +7 -7
- package/src/types/ComputedDataTree.ts +19 -19
- package/src/types/ContextObserver.ts +38 -38
- package/src/types/ContextObserverGrid.ts +33 -33
- package/src/types/ContextObserverMultiGrid.ts +27 -27
- package/src/types/ContextObserverMultiValue.ts +4 -4
- package/src/types/ContextObserverRelationship.ts +4 -4
- package/src/types/ContextObserverSeries.ts +7 -7
- package/src/types/ContextObserverTree.ts +10 -10
- package/src/types/ContextSubject.ts +18 -18
- package/src/types/Data.ts +45 -45
- package/src/types/DataFormatter.ts +95 -95
- package/src/types/DataFormatterGrid.ts +55 -55
- package/src/types/DataFormatterMultiGrid.ts +42 -42
- package/src/types/DataFormatterMultiValue.ts +20 -20
- package/src/types/DataFormatterRelationship.ts +22 -22
- package/src/types/DataFormatterSeries.ts +29 -29
- package/src/types/DataFormatterTree.ts +12 -12
- package/src/types/DataGrid.ts +11 -11
- package/src/types/DataMultiGrid.ts +6 -6
- package/src/types/DataMultiValue.ts +12 -12
- package/src/types/DataRelationship.ts +27 -27
- package/src/types/DataSeries.ts +11 -11
- package/src/types/DataTree.ts +20 -20
- package/src/types/Event.ts +153 -153
- package/src/types/Layout.ts +11 -11
- package/src/types/Padding.ts +5 -5
- package/src/types/Plugin.ts +60 -60
- package/src/types/TransformData.ts +7 -7
- package/src/types/index.ts +37 -37
- package/src/utils/commonUtils.ts +50 -50
- package/src/utils/d3Utils.ts +89 -89
- package/src/utils/index.ts +4 -4
- package/src/utils/observables.ts +181 -183
- package/src/utils/orbchartsUtils.ts +253 -253
- package/tsconfig.json +13 -13
- 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
|
}
|