@orbcharts/plugins-basic 3.0.6 → 3.0.7
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-plugins-basic.es.js +2332 -2316
- package/dist/orbcharts-plugins-basic.umd.js +32 -32
- package/lib/core-types.ts +7 -7
- package/lib/core.ts +6 -6
- package/lib/gridObservables.ts +6 -6
- package/lib/plugins-basic-types.ts +6 -6
- package/package.json +48 -48
- package/src/base/BaseBars.ts +765 -765
- package/src/base/BaseBarsTriangle.ts +676 -676
- package/src/base/BaseDots.ts +464 -464
- package/src/base/BaseGroupAxis.ts +691 -691
- package/src/base/BaseLegend.ts +684 -684
- package/src/base/BaseLineAreas.ts +629 -629
- package/src/base/BaseLines.ts +706 -706
- package/src/base/BaseOrdinalBubbles.ts +729 -729
- package/src/base/BaseRacingBars.ts +582 -582
- package/src/base/BaseRacingLabels.ts +404 -404
- package/src/base/BaseRacingValueLabels.ts +403 -403
- package/src/base/BaseStackedBars.ts +782 -782
- package/src/base/BaseTooltip.ts +408 -386
- package/src/base/BaseValueAxis.ts +600 -600
- package/src/base/BaseXAxis.ts +427 -427
- package/src/base/BaseXZoom.ts +241 -241
- package/src/base/BaseYAxis.ts +389 -389
- package/src/base/types.ts +2 -2
- package/src/const.ts +30 -30
- package/src/grid/defaults.ts +213 -213
- package/src/grid/gridObservables.ts +635 -635
- package/src/grid/index.ts +16 -16
- package/src/grid/plugins/Bars.ts +69 -69
- package/src/grid/plugins/BarsPN.ts +66 -66
- package/src/grid/plugins/BarsTriangle.ts +73 -73
- package/src/grid/plugins/Dots.ts +68 -68
- package/src/grid/plugins/GridLegend.ts +107 -107
- package/src/grid/plugins/GridTooltip.ts +66 -66
- package/src/grid/plugins/GroupAux.ts +1095 -1095
- package/src/grid/plugins/GroupAxis.ts +73 -73
- package/src/grid/plugins/GroupZoom.ts +218 -218
- package/src/grid/plugins/LineAreas.ts +65 -65
- package/src/grid/plugins/Lines.ts +59 -59
- package/src/grid/plugins/StackedBars.ts +64 -64
- package/src/grid/plugins/StackedValueAxis.ts +96 -96
- package/src/grid/plugins/ValueAxis.ts +94 -94
- package/src/index.ts +6 -6
- package/src/multiGrid/defaults.ts +244 -244
- package/src/multiGrid/index.ts +14 -14
- package/src/multiGrid/multiGridObservables.ts +50 -50
- package/src/multiGrid/plugins/MultiBars.ts +108 -108
- package/src/multiGrid/plugins/MultiBarsTriangle.ts +114 -114
- package/src/multiGrid/plugins/MultiDots.ts +102 -102
- package/src/multiGrid/plugins/MultiGridLegend.ts +169 -169
- package/src/multiGrid/plugins/MultiGridTooltip.ts +66 -66
- package/src/multiGrid/plugins/MultiGroupAxis.ts +137 -137
- package/src/multiGrid/plugins/MultiLineAreas.ts +107 -107
- package/src/multiGrid/plugins/MultiLines.ts +101 -101
- package/src/multiGrid/plugins/MultiStackedBars.ts +106 -106
- package/src/multiGrid/plugins/MultiStackedValueAxis.ts +134 -134
- package/src/multiGrid/plugins/MultiValueAxis.ts +134 -134
- package/src/multiGrid/plugins/OverlappingStackedValueAxes.ts +300 -300
- package/src/multiGrid/plugins/OverlappingValueAxes.ts +300 -300
- package/src/multiValue/defaults.ts +523 -523
- package/src/multiValue/index.ts +16 -16
- package/src/multiValue/multiValueObservables.ts +781 -781
- package/src/multiValue/plugins/MultiValueLegend.ts +107 -107
- package/src/multiValue/plugins/MultiValueTooltip.ts +66 -66
- package/src/multiValue/plugins/OrdinalAux.ts +660 -660
- package/src/multiValue/plugins/OrdinalAxis.ts +524 -524
- package/src/multiValue/plugins/OrdinalBubbles.ts +226 -226
- package/src/multiValue/plugins/OrdinalZoom.ts +57 -57
- package/src/multiValue/plugins/RacingBars.ts +375 -375
- package/src/multiValue/plugins/RacingCounterTexts.ts +300 -300
- package/src/multiValue/plugins/RacingValueAxis.ts +114 -114
- package/src/multiValue/plugins/Scatter.ts +486 -486
- package/src/multiValue/plugins/ScatterBubbles.ts +635 -635
- package/src/multiValue/plugins/XAxis.ts +107 -107
- package/src/multiValue/plugins/XYAux.ts +683 -683
- package/src/multiValue/plugins/XYAxes.ts +194 -194
- package/src/multiValue/plugins/XYAxes_legacy.ts +683 -683
- package/src/multiValue/plugins/XZoom.ts +40 -40
- package/src/noneData/defaults.ts +102 -102
- package/src/noneData/index.ts +3 -3
- package/src/noneData/plugins/Container.ts +27 -27
- package/src/noneData/plugins/Tooltip.ts +373 -373
- package/src/relationship/defaults.ts +221 -221
- package/src/relationship/index.ts +5 -5
- package/src/relationship/plugins/ForceDirected.ts +1056 -1056
- package/src/relationship/plugins/ForceDirectedBubbles.ts +1294 -1294
- package/src/relationship/plugins/RelationshipLegend.ts +100 -100
- package/src/relationship/plugins/RelationshipTooltip.ts +66 -66
- package/src/relationship/relationshipObservables.ts +49 -49
- package/src/series/defaults.ts +223 -223
- package/src/series/index.ts +9 -9
- package/src/series/plugins/Bubbles.ts +784 -781
- package/src/series/plugins/Pie.ts +622 -622
- package/src/series/plugins/PieEventTexts.ts +283 -283
- package/src/series/plugins/PieLabels.ts +639 -639
- package/src/series/plugins/Rose.ts +515 -515
- package/src/series/plugins/RoseLabels.ts +599 -599
- package/src/series/plugins/SeriesLegend.ts +107 -107
- package/src/series/plugins/SeriesTooltip.ts +66 -66
- package/src/series/seriesObservables.ts +168 -168
- package/src/series/seriesUtils.ts +51 -51
- package/src/tree/defaults.ts +102 -102
- package/src/tree/index.ts +4 -4
- package/src/tree/plugins/TreeLegend.ts +100 -100
- package/src/tree/plugins/TreeMap.ts +341 -341
- package/src/tree/plugins/TreeTooltip.ts +66 -66
- package/src/utils/commonUtils.ts +31 -31
- package/src/utils/d3Graphics.ts +176 -176
- package/src/utils/d3Utils.ts +92 -92
- package/src/utils/observables.ts +14 -14
- package/src/utils/orbchartsUtils.ts +129 -129
- package/tsconfig.base.json +13 -13
- package/tsconfig.json +2 -2
- package/vite.config.js +22 -22
@@ -1,636 +1,636 @@
|
|
1
|
-
import * as d3 from 'd3'
|
2
|
-
import {
|
3
|
-
Observable,
|
4
|
-
Subject,
|
5
|
-
of,
|
6
|
-
takeUntil,
|
7
|
-
filter,
|
8
|
-
map,
|
9
|
-
switchMap,
|
10
|
-
combineLatest,
|
11
|
-
merge,
|
12
|
-
shareReplay,
|
13
|
-
distinctUntilChanged
|
14
|
-
} from 'rxjs'
|
15
|
-
import type {
|
16
|
-
ChartParams,
|
17
|
-
HighlightTarget,
|
18
|
-
DataFormatterGrid,
|
19
|
-
ComputedDataGrid,
|
20
|
-
ComputedDatumGrid,
|
21
|
-
ContainerSize,
|
22
|
-
TransformData,
|
23
|
-
ContainerPositionScaled,
|
24
|
-
Layout } from '../../lib/core-types'
|
25
|
-
import { createAxisToLabelIndexScale } from '../../lib/core'
|
26
|
-
import { getClassName, getUniID } from '../utils/orbchartsUtils'
|
27
|
-
import { d3EventObservable } from '../utils/observables'
|
28
|
-
|
29
|
-
// 建立 grid 主要的 selection
|
30
|
-
export const gridSelectionsObservable = ({ selection, pluginName, clipPathID, seriesLabels$, gridContainerPosition$, gridAxesTransform$, gridGraphicTransform$ }: {
|
31
|
-
selection: d3.Selection<any, unknown, any, unknown>
|
32
|
-
pluginName: string
|
33
|
-
clipPathID: string
|
34
|
-
// computedData$: Observable<ComputedDataGrid>
|
35
|
-
seriesLabels$: Observable<string[]>
|
36
|
-
gridContainerPosition$: Observable<ContainerPositionScaled[]>
|
37
|
-
gridAxesTransform$: Observable<TransformData>
|
38
|
-
gridGraphicTransform$: Observable<TransformData>
|
39
|
-
}) => {
|
40
|
-
const seriesClassName = getClassName(pluginName, 'series')
|
41
|
-
const axesClassName = getClassName(pluginName, 'axes')
|
42
|
-
const graphicClassName = getClassName(pluginName, 'graphic')
|
43
|
-
|
44
|
-
// <g> series selection(container排放位置)
|
45
|
-
// <g> axes selection(旋轉圖軸方向)
|
46
|
-
// <defs> clipPath selection
|
47
|
-
// <g> graphic selection(圖形 scale 範圍的變形)
|
48
|
-
const seriesSelection$ = seriesLabels$.pipe(
|
49
|
-
map((seriesLabels, i) => {
|
50
|
-
return selection
|
51
|
-
.selectAll<SVGGElement, string>(`g.${seriesClassName}`)
|
52
|
-
.data(seriesLabels, d => d)
|
53
|
-
.join(
|
54
|
-
enter => {
|
55
|
-
return enter
|
56
|
-
.append('g')
|
57
|
-
.classed(seriesClassName, true)
|
58
|
-
.each((d, i, g) => {
|
59
|
-
const axesSelection = d3.select(g[i])
|
60
|
-
.selectAll<SVGGElement, ComputedDatumGrid[]>(`g.${axesClassName}`)
|
61
|
-
.data([i])
|
62
|
-
.join(
|
63
|
-
enter => {
|
64
|
-
return enter
|
65
|
-
.append('g')
|
66
|
-
.classed(axesClassName, true)
|
67
|
-
.attr('clip-path', `url(#${clipPathID})`)
|
68
|
-
.each((d, i, g) => {
|
69
|
-
const defsSelection = d3.select(g[i])
|
70
|
-
.selectAll<SVGDefsElement, any>('defs')
|
71
|
-
.data([i])
|
72
|
-
.join('defs')
|
73
|
-
|
74
|
-
const graphicGSelection = d3.select(g[i])
|
75
|
-
.selectAll<SVGGElement, any>('g')
|
76
|
-
.data([i])
|
77
|
-
.join('g')
|
78
|
-
.classed(graphicClassName, true)
|
79
|
-
})
|
80
|
-
},
|
81
|
-
update => update,
|
82
|
-
exit => exit.remove()
|
83
|
-
)
|
84
|
-
})
|
85
|
-
},
|
86
|
-
update => update,
|
87
|
-
exit => exit.remove()
|
88
|
-
)
|
89
|
-
}),
|
90
|
-
shareReplay(1)
|
91
|
-
)
|
92
|
-
|
93
|
-
// <g> series selection
|
94
|
-
combineLatest({
|
95
|
-
seriesSelection: seriesSelection$,
|
96
|
-
gridContainerPosition: gridContainerPosition$
|
97
|
-
}).pipe(
|
98
|
-
switchMap(async d => d)
|
99
|
-
).subscribe(data => {
|
100
|
-
data.seriesSelection
|
101
|
-
.transition()
|
102
|
-
.attr('transform', (d, i) => {
|
103
|
-
const gridContainerPosition = data.gridContainerPosition[i] ?? data.gridContainerPosition[0]
|
104
|
-
const translate = gridContainerPosition.translate
|
105
|
-
const scale = gridContainerPosition.scale
|
106
|
-
return `translate(${translate[0]}, ${translate[1]}) scale(${scale[0]}, ${scale[1]})`
|
107
|
-
})
|
108
|
-
})
|
109
|
-
|
110
|
-
// <g> axes selection
|
111
|
-
const axesSelection$ = combineLatest({
|
112
|
-
seriesSelection: seriesSelection$,
|
113
|
-
gridAxesTransform: gridAxesTransform$
|
114
|
-
}).pipe(
|
115
|
-
switchMap(async d => d),
|
116
|
-
map(data => {
|
117
|
-
return data.seriesSelection
|
118
|
-
.select<SVGGElement>(`g.${axesClassName}`)
|
119
|
-
.style('transform', data.gridAxesTransform.value)
|
120
|
-
}),
|
121
|
-
shareReplay(1)
|
122
|
-
)
|
123
|
-
|
124
|
-
// <defs> clipPath selection
|
125
|
-
const defsSelection$ = axesSelection$.pipe(
|
126
|
-
map(axesSelection => {
|
127
|
-
return axesSelection.select<SVGDefsElement>('defs')
|
128
|
-
}),
|
129
|
-
shareReplay(1)
|
130
|
-
)
|
131
|
-
|
132
|
-
// <g> graphic selection
|
133
|
-
const graphicGSelection$ = combineLatest({
|
134
|
-
axesSelection: axesSelection$,
|
135
|
-
gridGraphicTransform: gridGraphicTransform$
|
136
|
-
}).pipe(
|
137
|
-
switchMap(async d => d),
|
138
|
-
map(data => {
|
139
|
-
const graphicGSelection = data.axesSelection
|
140
|
-
.select<SVGGElement>(`g.${graphicClassName}`)
|
141
|
-
graphicGSelection
|
142
|
-
.transition()
|
143
|
-
.duration(50)
|
144
|
-
.style('transform', data.gridGraphicTransform.value)
|
145
|
-
return graphicGSelection
|
146
|
-
}),
|
147
|
-
shareReplay(1)
|
148
|
-
)
|
149
|
-
|
150
|
-
return {
|
151
|
-
seriesSelection$,
|
152
|
-
axesSelection$,
|
153
|
-
defsSelection$,
|
154
|
-
graphicGSelection$
|
155
|
-
}
|
156
|
-
}
|
157
|
-
|
158
|
-
// 建立 grid 主要的 selection - 只取的container
|
159
|
-
export const gridContainerSelectionsObservable = ({ selection, pluginName, computedData$, gridContainerPosition$, isSeriesSeprate$ }: {
|
160
|
-
selection: d3.Selection<any, unknown, any, unknown>
|
161
|
-
pluginName: string
|
162
|
-
computedData$: Observable<ComputedDataGrid>
|
163
|
-
gridContainerPosition$: Observable<ContainerPositionScaled[]>
|
164
|
-
isSeriesSeprate$: Observable<boolean>
|
165
|
-
}) => {
|
166
|
-
const containerClassName = getClassName(pluginName, 'container')
|
167
|
-
|
168
|
-
const containerSelection$ = combineLatest({
|
169
|
-
computedData: computedData$.pipe(
|
170
|
-
distinctUntilChanged((a, b) => {
|
171
|
-
// 只有當series的數量改變時,才重新計算
|
172
|
-
return a.length === b.length
|
173
|
-
}),
|
174
|
-
),
|
175
|
-
isSeriesSeprate: isSeriesSeprate$
|
176
|
-
}).pipe(
|
177
|
-
switchMap(async (d) => d),
|
178
|
-
map(data => {
|
179
|
-
return data.isSeriesSeprate
|
180
|
-
// series分開的時候顯示各別axis
|
181
|
-
? data.computedData
|
182
|
-
// series合併的時候只顯示第一個axis
|
183
|
-
: [data.computedData[0]]
|
184
|
-
}),
|
185
|
-
map((computedData, i) => {
|
186
|
-
return selection
|
187
|
-
.selectAll<SVGGElement, ComputedDatumGrid[]>(`g.${containerClassName}`)
|
188
|
-
.data(computedData, d => (d && d[0]) ? d[0].seriesIndex : i)
|
189
|
-
.join('g')
|
190
|
-
.classed(containerClassName, true)
|
191
|
-
}),
|
192
|
-
shareReplay(1)
|
193
|
-
)
|
194
|
-
|
195
|
-
combineLatest({
|
196
|
-
containerSelection: containerSelection$,
|
197
|
-
gridContainerPosition: gridContainerPosition$
|
198
|
-
}).pipe(
|
199
|
-
switchMap(async d => d)
|
200
|
-
).subscribe(data => {
|
201
|
-
data.containerSelection
|
202
|
-
.attr('transform', (d, i) => {
|
203
|
-
const gridContainerPosition = data.gridContainerPosition[i] ?? data.gridContainerPosition[0]
|
204
|
-
const translate = gridContainerPosition.translate
|
205
|
-
const scale = gridContainerPosition.scale
|
206
|
-
return `translate(${translate[0]}, ${translate[1]}) scale(${scale[0]}, ${scale[1]})`
|
207
|
-
})
|
208
|
-
// .attr('opacity', 0)
|
209
|
-
// .transition()
|
210
|
-
// .attr('opacity', 1)
|
211
|
-
})
|
212
|
-
|
213
|
-
return containerSelection$
|
214
|
-
}
|
215
|
-
|
216
|
-
// 由事件取得group data的function
|
217
|
-
export const gridGroupPositionFnObservable = ({ fullDataFormatter$, gridAxesSize$, computedData$, fullChartParams$, gridContainerPosition$, layout$ }: {
|
218
|
-
fullDataFormatter$: Observable<DataFormatterGrid>
|
219
|
-
gridAxesSize$: Observable<{
|
220
|
-
width: number;
|
221
|
-
height: number;
|
222
|
-
}>
|
223
|
-
computedData$: Observable<ComputedDataGrid>
|
224
|
-
// GroupDataMap$: Observable<Map<string, ComputedDatumGrid[]>>
|
225
|
-
fullChartParams$: Observable<ChartParams>
|
226
|
-
gridContainerPosition$: Observable<ContainerPositionScaled[]>
|
227
|
-
layout$: Observable<Layout>
|
228
|
-
}): Observable<(event: any) => { groupIndex: number; groupLabel: string }> => {
|
229
|
-
const destroy$ = new Subject()
|
230
|
-
|
231
|
-
// 顯示範圍內的group labels
|
232
|
-
// const scaleRangeGroupLabels$: Observable<string[]> = new Observable(subscriber => {
|
233
|
-
// combineLatest({
|
234
|
-
// dataFormatter: fullDataFormatter$,
|
235
|
-
// computedData: computedData$
|
236
|
-
// }).pipe(
|
237
|
-
// takeUntil(destroy$),
|
238
|
-
// switchMap(async (d) => d),
|
239
|
-
// ).subscribe(data => {
|
240
|
-
// const groupMin = 0
|
241
|
-
// const groupMax = data.computedData[0] ? data.computedData[0].length - 1 : 0
|
242
|
-
// const groupScaleDomainMin = data.dataFormatter.groupAxis.scaleDomain[0] === 'auto'
|
243
|
-
// ? groupMin - data.dataFormatter.groupAxis.scalePadding
|
244
|
-
// : data.dataFormatter.groupAxis.scaleDomain[0] as number - data.dataFormatter.groupAxis.scalePadding
|
245
|
-
// const groupScaleDomainMax = data.dataFormatter.groupAxis.scaleDomain[1] === 'auto'
|
246
|
-
// ? groupMax + data.dataFormatter.groupAxis.scalePadding
|
247
|
-
// : data.dataFormatter.groupAxis.scaleDomain[1] as number + data.dataFormatter.groupAxis.scalePadding
|
248
|
-
|
249
|
-
// // const groupingAmount = data.computedData[0]
|
250
|
-
// // ? data.computedData[0].length
|
251
|
-
// // : 0
|
252
|
-
|
253
|
-
// let _labels = data.dataFormatter.seriesDirection === 'row'
|
254
|
-
// ? (data.computedData[0] ?? []).map(d => d.groupLabel)
|
255
|
-
// : data.computedData.map(d => d[0].groupLabel)
|
256
|
-
|
257
|
-
// const _axisLabels =
|
258
|
-
// // new Array(groupingAmount).fill(0)
|
259
|
-
// // .map((d, i) => {
|
260
|
-
// // return _labels[i] != null
|
261
|
-
// // ? _labels[i]
|
262
|
-
// // : String(i) // 沒有label則用序列號填充
|
263
|
-
// // })
|
264
|
-
// _labels
|
265
|
-
// .filter((d, i) => {
|
266
|
-
// return i >= groupScaleDomainMin && i <= groupScaleDomainMax
|
267
|
-
// })
|
268
|
-
// subscriber.next(_axisLabels)
|
269
|
-
// })
|
270
|
-
// })
|
271
|
-
const groupScaleDomain$ = combineLatest({
|
272
|
-
fullDataFormatter: fullDataFormatter$,
|
273
|
-
gridAxesSize: gridAxesSize$,
|
274
|
-
computedData: computedData$
|
275
|
-
}).pipe(
|
276
|
-
switchMap(async (d) => d),
|
277
|
-
map(data => {
|
278
|
-
const groupMin = 0
|
279
|
-
const groupMax = data.computedData[0] ? data.computedData[0].length - 1 : 0
|
280
|
-
// const groupScaleDomainMin = data.fullDataFormatter.groupAxis.scaleDomain[0] === 'auto'
|
281
|
-
// ? groupMin - data.fullDataFormatter.groupAxis.scalePadding
|
282
|
-
// : data.fullDataFormatter.groupAxis.scaleDomain[0] as number - data.fullDataFormatter.groupAxis.scalePadding
|
283
|
-
const groupScaleDomainMin = data.fullDataFormatter.groupAxis.scaleDomain[0] - data.fullDataFormatter.groupAxis.scalePadding
|
284
|
-
const groupScaleDomainMax = data.fullDataFormatter.groupAxis.scaleDomain[1] === 'max'
|
285
|
-
? groupMax + data.fullDataFormatter.groupAxis.scalePadding
|
286
|
-
: data.fullDataFormatter.groupAxis.scaleDomain[1] as number + data.fullDataFormatter.groupAxis.scalePadding
|
287
|
-
|
288
|
-
return [groupScaleDomainMin, groupScaleDomainMax]
|
289
|
-
}),
|
290
|
-
shareReplay(1)
|
291
|
-
)
|
292
|
-
|
293
|
-
const groupLabels$ = combineLatest({
|
294
|
-
fullDataFormatter: fullDataFormatter$,
|
295
|
-
computedData: computedData$
|
296
|
-
}).pipe(
|
297
|
-
switchMap(async d => d),
|
298
|
-
map(data => {
|
299
|
-
return data.fullDataFormatter.seriesDirection === 'row'
|
300
|
-
? (data.computedData[0] ?? []).map(d => d.groupLabel)
|
301
|
-
: data.computedData.map(d => d[0].groupLabel)
|
302
|
-
})
|
303
|
-
)
|
304
|
-
|
305
|
-
// 顯示範圍內的group labels
|
306
|
-
const scaleRangeGroupLabels$ = combineLatest({
|
307
|
-
groupScaleDomain: groupScaleDomain$,
|
308
|
-
groupLabels: groupLabels$
|
309
|
-
}).pipe(
|
310
|
-
switchMap(async d => d),
|
311
|
-
map(data => {
|
312
|
-
return data.groupLabels
|
313
|
-
.filter((d, i) => {
|
314
|
-
return i >= data.groupScaleDomain[0] && i <= data.groupScaleDomain[1]
|
315
|
-
})
|
316
|
-
})
|
317
|
-
)
|
318
|
-
|
319
|
-
const columnAmount$ = gridContainerPosition$.pipe(
|
320
|
-
map(gridContainerPosition => {
|
321
|
-
const maxColumnIndex = gridContainerPosition.reduce((acc, current) => {
|
322
|
-
return current.columnIndex > acc ? current.columnIndex : acc
|
323
|
-
}, 0)
|
324
|
-
return maxColumnIndex + 1
|
325
|
-
}),
|
326
|
-
distinctUntilChanged()
|
327
|
-
)
|
328
|
-
|
329
|
-
const rowAmount$ = gridContainerPosition$.pipe(
|
330
|
-
map(gridContainerPosition => {
|
331
|
-
const maxRowIndex = gridContainerPosition.reduce((acc, current) => {
|
332
|
-
return current.rowIndex > acc ? current.rowIndex : acc
|
333
|
-
}, 0)
|
334
|
-
return maxRowIndex + 1
|
335
|
-
}),
|
336
|
-
distinctUntilChanged()
|
337
|
-
)
|
338
|
-
|
339
|
-
return new Observable<(event: any) => { groupIndex: number; groupLabel: string }>(subscriber => {
|
340
|
-
combineLatest({
|
341
|
-
dataFormatter: fullDataFormatter$,
|
342
|
-
axisSize: gridAxesSize$,
|
343
|
-
fullChartParams: fullChartParams$,
|
344
|
-
scaleRangeGroupLabels: scaleRangeGroupLabels$,
|
345
|
-
groupLabels: groupLabels$,
|
346
|
-
groupScaleDomain: groupScaleDomain$,
|
347
|
-
columnAmount: columnAmount$,
|
348
|
-
rowAmount: rowAmount$,
|
349
|
-
layout: layout$
|
350
|
-
}).pipe(
|
351
|
-
takeUntil(destroy$),
|
352
|
-
switchMap(async (d) => d),
|
353
|
-
).subscribe(data => {
|
354
|
-
|
355
|
-
const reverse = data.dataFormatter.valueAxis.position === 'right'
|
356
|
-
|| data.dataFormatter.valueAxis.position === 'bottom'
|
357
|
-
? true : false
|
358
|
-
|
359
|
-
// 比例尺座標對應非連續資料索引
|
360
|
-
const xIndexScale = createAxisToLabelIndexScale({
|
361
|
-
axisLabels: data.scaleRangeGroupLabels,
|
362
|
-
axisWidth: data.axisSize.width,
|
363
|
-
padding: data.dataFormatter.groupAxis.scalePadding,
|
364
|
-
reverse
|
365
|
-
})
|
366
|
-
|
367
|
-
// 依比例尺位置計算座標
|
368
|
-
const axisValuePredicate = (event: any) => {
|
369
|
-
return data.dataFormatter.groupAxis.position === 'bottom'
|
370
|
-
|| data.dataFormatter.groupAxis.position === 'top'
|
371
|
-
? event.offsetX - data.fullChartParams.padding.left
|
372
|
-
: event.offsetY - data.fullChartParams.padding.top
|
373
|
-
}
|
374
|
-
|
375
|
-
// 比例尺座標取得groupData的function
|
376
|
-
const createEventGroupData: (event: MouseEvent) => { groupIndex: number; groupLabel: string } = (event: any) => {
|
377
|
-
// 由於event座標是基於底層的,但是container會有多欄,所以要重新計算
|
378
|
-
const eventData = {
|
379
|
-
offsetX: event.offsetX * data.columnAmount % data.layout.rootWidth,
|
380
|
-
offsetY: event.offsetY * data.rowAmount % data.layout.rootHeight
|
381
|
-
}
|
382
|
-
// console.log('data.columnAmount', data.columnAmount, 'data.rowAmount', data.rowAmount, 'data.layout.rootWidth', data.layout.rootWidth, 'data.layout.rootHeight', data.layout.rootHeight)
|
383
|
-
const axisValue = axisValuePredicate(eventData)
|
384
|
-
const xIndex = xIndexScale(axisValue)
|
385
|
-
const currentxIndexStart = Math.ceil(data.groupScaleDomain[0]) // 因為有padding所以會有小數點,所以要無條件進位
|
386
|
-
const groupIndex = xIndex + currentxIndexStart
|
387
|
-
|
388
|
-
return {
|
389
|
-
groupIndex,
|
390
|
-
groupLabel: data.groupLabels[groupIndex] ?? ''
|
391
|
-
}
|
392
|
-
}
|
393
|
-
|
394
|
-
subscriber.next(createEventGroupData)
|
395
|
-
|
396
|
-
return function unsubscribe () {
|
397
|
-
destroy$.next(undefined)
|
398
|
-
}
|
399
|
-
})
|
400
|
-
})
|
401
|
-
}
|
402
|
-
|
403
|
-
export const gridGroupPositionObservable = ({ rootSelection, fullDataFormatter$, containerSize$, gridAxesContainerSize$, computedData$, gridContainerPosition$, layout$ }: {
|
404
|
-
rootSelection: d3.Selection<any, unknown, any, unknown>
|
405
|
-
fullDataFormatter$: Observable<DataFormatterGrid>
|
406
|
-
// gridAxesSize$: Observable<ContainerSize>
|
407
|
-
containerSize$: Observable<ContainerSize>
|
408
|
-
gridAxesContainerSize$: Observable<ContainerSize>
|
409
|
-
computedData$: Observable<ComputedDataGrid>
|
410
|
-
gridContainerPosition$: Observable<ContainerPositionScaled[]>
|
411
|
-
layout$: Observable<Layout>
|
412
|
-
}) => {
|
413
|
-
const rootMousemove$ = d3EventObservable(rootSelection, 'mousemove')
|
414
|
-
|
415
|
-
const groupScaleDomain$ = combineLatest({
|
416
|
-
fullDataFormatter: fullDataFormatter$,
|
417
|
-
// gridAxesSize: gridAxesSize$,
|
418
|
-
computedData: computedData$
|
419
|
-
}).pipe(
|
420
|
-
switchMap(async (d) => d),
|
421
|
-
map(data => {
|
422
|
-
const groupMin = 0
|
423
|
-
const groupMax = data.computedData[0] ? data.computedData[0].length - 1 : 0
|
424
|
-
// const groupScaleDomainMin = data.fullDataFormatter.groupAxis.scaleDomain[0] === 'auto'
|
425
|
-
// ? groupMin - data.fullDataFormatter.groupAxis.scalePadding
|
426
|
-
// : data.fullDataFormatter.groupAxis.scaleDomain[0] as number - data.fullDataFormatter.groupAxis.scalePadding
|
427
|
-
const groupScaleDomainMin = data.fullDataFormatter.groupAxis.scaleDomain[0] - data.fullDataFormatter.groupAxis.scalePadding
|
428
|
-
const groupScaleDomainMax = data.fullDataFormatter.groupAxis.scaleDomain[1] === 'max'
|
429
|
-
? groupMax + data.fullDataFormatter.groupAxis.scalePadding
|
430
|
-
: data.fullDataFormatter.groupAxis.scaleDomain[1] as number + data.fullDataFormatter.groupAxis.scalePadding
|
431
|
-
|
432
|
-
return [groupScaleDomainMin, groupScaleDomainMax]
|
433
|
-
}),
|
434
|
-
shareReplay(1)
|
435
|
-
)
|
436
|
-
|
437
|
-
const groupLabels$ = combineLatest({
|
438
|
-
fullDataFormatter: fullDataFormatter$,
|
439
|
-
computedData: computedData$
|
440
|
-
}).pipe(
|
441
|
-
switchMap(async d => d),
|
442
|
-
map(data => {
|
443
|
-
return data.fullDataFormatter.seriesDirection === 'row'
|
444
|
-
? (data.computedData[0] ?? []).map(d => d.groupLabel)
|
445
|
-
: data.computedData.map(d => d[0].groupLabel)
|
446
|
-
})
|
447
|
-
)
|
448
|
-
|
449
|
-
const scaleRangeGroupLabels$ = combineLatest({
|
450
|
-
groupScaleDomain: groupScaleDomain$,
|
451
|
-
groupLabels: groupLabels$
|
452
|
-
}).pipe(
|
453
|
-
switchMap(async d => d),
|
454
|
-
map(data => {
|
455
|
-
return data.groupLabels
|
456
|
-
.filter((d, i) => {
|
457
|
-
return i >= data.groupScaleDomain[0] && i <= data.groupScaleDomain[1]
|
458
|
-
})
|
459
|
-
})
|
460
|
-
)
|
461
|
-
|
462
|
-
const reverse$ = fullDataFormatter$.pipe(
|
463
|
-
map(d => {
|
464
|
-
return d.valueAxis.position === 'right' || d.valueAxis.position === 'bottom'
|
465
|
-
? true
|
466
|
-
: false
|
467
|
-
})
|
468
|
-
)
|
469
|
-
|
470
|
-
// 比例尺座標對應非連續資料索引
|
471
|
-
const xIndexScale$ = combineLatest({
|
472
|
-
reverse: reverse$,
|
473
|
-
// gridAxesSize: gridAxesSize$,
|
474
|
-
gridAxesContainerSize: gridAxesContainerSize$,
|
475
|
-
scaleRangeGroupLabels: scaleRangeGroupLabels$,
|
476
|
-
fullDataFormatter: fullDataFormatter$
|
477
|
-
}).pipe(
|
478
|
-
switchMap(async d => d),
|
479
|
-
map(data => {
|
480
|
-
return createAxisToLabelIndexScale({
|
481
|
-
axisLabels: data.scaleRangeGroupLabels,
|
482
|
-
axisWidth: data.gridAxesContainerSize.width,
|
483
|
-
padding: data.fullDataFormatter.groupAxis.scalePadding,
|
484
|
-
reverse: data.reverse
|
485
|
-
})
|
486
|
-
})
|
487
|
-
)
|
488
|
-
|
489
|
-
// const columnAmount$ = gridContainerPosition$.pipe(
|
490
|
-
// map(gridContainerPosition => {
|
491
|
-
// const maxColumnIndex = gridContainerPosition.reduce((acc, current) => {
|
492
|
-
// return current.columnIndex > acc ? current.columnIndex : acc
|
493
|
-
// }, 0)
|
494
|
-
// return maxColumnIndex + 1
|
495
|
-
// }),
|
496
|
-
// distinctUntilChanged()
|
497
|
-
// )
|
498
|
-
|
499
|
-
// const rowAmount$ = gridContainerPosition$.pipe(
|
500
|
-
// map(gridContainerPosition => {
|
501
|
-
// const maxRowIndex = gridContainerPosition.reduce((acc, current) => {
|
502
|
-
// return current.rowIndex > acc ? current.rowIndex : acc
|
503
|
-
// }, 0)
|
504
|
-
// return maxRowIndex + 1
|
505
|
-
// }),
|
506
|
-
// distinctUntilChanged()
|
507
|
-
// )
|
508
|
-
|
509
|
-
const axisValue$ = combineLatest({
|
510
|
-
fullDataFormatter: fullDataFormatter$,
|
511
|
-
rootMousemove: rootMousemove$,
|
512
|
-
// containerSize: containerSize$,
|
513
|
-
gridContainerPosition: gridContainerPosition$,
|
514
|
-
// columnAmount: columnAmount$,
|
515
|
-
// rowAmount: rowAmount$,
|
516
|
-
layout: layout$
|
517
|
-
}).pipe(
|
518
|
-
switchMap(async d => d),
|
519
|
-
map(data => {
|
520
|
-
// // 由於event座標是基於底層的,但是container會有多欄,所以要重新計算
|
521
|
-
// const eventData = {
|
522
|
-
// offsetX: data.rootMousemove.offsetX * data.columnAmount % data.layout.rootWidth,
|
523
|
-
// offsetY: data.rootMousemove.offsetY * data.rowAmount % data.layout.rootHeight
|
524
|
-
// }
|
525
|
-
// return data.fullDataFormatter.groupAxis.position === 'bottom'
|
526
|
-
// || data.fullDataFormatter.groupAxis.position === 'top'
|
527
|
-
// ? eventData.offsetX - data.layout.left
|
528
|
-
// : eventData.offsetY - data.layout.top
|
529
|
-
|
530
|
-
if (data.fullDataFormatter.groupAxis.position === 'bottom' || data.fullDataFormatter.groupAxis.position === 'top') {
|
531
|
-
let x = data.rootMousemove.offsetX
|
532
|
-
const rangeArr = data.gridContainerPosition
|
533
|
-
.map((d, i) => [d.translate[0], data.gridContainerPosition[i + 1]?.translate[0] ?? data.layout.rootWidth])
|
534
|
-
.filter(d => d[0] < d[1])
|
535
|
-
const range = rangeArr.find(d => x >= d[0] && x <= d[1])
|
536
|
-
if (range) {
|
537
|
-
x = x - range[0]
|
538
|
-
}
|
539
|
-
return x - data.layout.left
|
540
|
-
} else {
|
541
|
-
let y = data.rootMousemove.offsetY
|
542
|
-
const rangeArr = data.gridContainerPosition
|
543
|
-
.map((d, i) => [d.translate[1], data.gridContainerPosition[i + 1]?.translate[1] ?? data.layout.rootHeight])
|
544
|
-
.filter(d => d[0] < d[1])
|
545
|
-
const range = rangeArr.find(d => y >= d[0] && y <= d[1])
|
546
|
-
if (range) {
|
547
|
-
y = y - range[0]
|
548
|
-
}
|
549
|
-
return y - data.layout.top
|
550
|
-
}
|
551
|
-
})
|
552
|
-
)
|
553
|
-
|
554
|
-
const groupIndex$ = combineLatest({
|
555
|
-
xIndexScale: xIndexScale$,
|
556
|
-
axisValue: axisValue$,
|
557
|
-
groupScaleDomain: groupScaleDomain$
|
558
|
-
}).pipe(
|
559
|
-
switchMap(async d => d),
|
560
|
-
map(data => {
|
561
|
-
const xIndex = data.xIndexScale(data.axisValue)
|
562
|
-
const currentxIndexStart = Math.ceil(data.groupScaleDomain[0]) // 因為有padding所以會有小數點,所以要無條件進位
|
563
|
-
return xIndex + currentxIndexStart
|
564
|
-
})
|
565
|
-
)
|
566
|
-
|
567
|
-
const groupLabel$ = combineLatest({
|
568
|
-
groupIndex: groupIndex$,
|
569
|
-
groupLabels: groupLabels$
|
570
|
-
}).pipe(
|
571
|
-
switchMap(async d => d),
|
572
|
-
map(data => {
|
573
|
-
return data.groupLabels[data.groupIndex] ?? ''
|
574
|
-
})
|
575
|
-
)
|
576
|
-
|
577
|
-
return combineLatest({
|
578
|
-
groupIndex: groupIndex$,
|
579
|
-
groupLabel: groupLabel$
|
580
|
-
}).pipe(
|
581
|
-
switchMap(async d => d),
|
582
|
-
map(data => {
|
583
|
-
return {
|
584
|
-
groupIndex: data.groupIndex,
|
585
|
-
groupLabel: data.groupLabel
|
586
|
-
}
|
587
|
-
})
|
588
|
-
)
|
589
|
-
}
|
590
|
-
|
591
|
-
// const gridContainerEventData$ = ({ eventData$, gridContainerPosition$, layout$ }: {
|
592
|
-
// eventData$: Observable<any>
|
593
|
-
// gridContainerPosition$: Observable<ContainerPositionScaled[]>
|
594
|
-
// layout$: Observable<Layout>
|
595
|
-
// }): Observable<{
|
596
|
-
// offsetX: number;
|
597
|
-
// offsetY: number;
|
598
|
-
// }> => {
|
599
|
-
// const columnAmount$ = gridContainerPosition$.pipe(
|
600
|
-
// map(gridContainerPosition => {
|
601
|
-
// const maxColumnIndex = gridContainerPosition.reduce((acc, current) => {
|
602
|
-
// return current.columnIndex > acc ? current.columnIndex : acc
|
603
|
-
// }, 0)
|
604
|
-
// return maxColumnIndex + 1
|
605
|
-
// }),
|
606
|
-
// distinctUntilChanged()
|
607
|
-
// )
|
608
|
-
|
609
|
-
// const rowAmount$ = gridContainerPosition$.pipe(
|
610
|
-
// map(gridContainerPosition => {
|
611
|
-
// const maxRowIndex = gridContainerPosition.reduce((acc, current) => {
|
612
|
-
// return current.rowIndex > acc ? current.rowIndex : acc
|
613
|
-
// }, 0)
|
614
|
-
// return maxRowIndex + 1
|
615
|
-
// }),
|
616
|
-
// distinctUntilChanged()
|
617
|
-
// )
|
618
|
-
|
619
|
-
// return combineLatest({
|
620
|
-
// eventData: eventData$,
|
621
|
-
// gridContainerPosition: gridContainerPosition$,
|
622
|
-
// layout: layout$,
|
623
|
-
// columnAmount: columnAmount$,
|
624
|
-
// rowAmount: rowAmount$
|
625
|
-
// }).pipe(
|
626
|
-
// switchMap(async d => d),
|
627
|
-
// map(data => {
|
628
|
-
// // 由於event座標是基於底層的,但是container會有多欄,所以要重新計算
|
629
|
-
// const eventData = {
|
630
|
-
// offsetX: data.eventData.offsetX * data.columnAmount % data.layout.rootWidth,
|
631
|
-
// offsetY: data.eventData.offsetY * data.rowAmount % data.layout.rootHeight
|
632
|
-
// }
|
633
|
-
// return eventData
|
634
|
-
// })
|
635
|
-
// )
|
1
|
+
import * as d3 from 'd3'
|
2
|
+
import {
|
3
|
+
Observable,
|
4
|
+
Subject,
|
5
|
+
of,
|
6
|
+
takeUntil,
|
7
|
+
filter,
|
8
|
+
map,
|
9
|
+
switchMap,
|
10
|
+
combineLatest,
|
11
|
+
merge,
|
12
|
+
shareReplay,
|
13
|
+
distinctUntilChanged
|
14
|
+
} from 'rxjs'
|
15
|
+
import type {
|
16
|
+
ChartParams,
|
17
|
+
HighlightTarget,
|
18
|
+
DataFormatterGrid,
|
19
|
+
ComputedDataGrid,
|
20
|
+
ComputedDatumGrid,
|
21
|
+
ContainerSize,
|
22
|
+
TransformData,
|
23
|
+
ContainerPositionScaled,
|
24
|
+
Layout } from '../../lib/core-types'
|
25
|
+
import { createAxisToLabelIndexScale } from '../../lib/core'
|
26
|
+
import { getClassName, getUniID } from '../utils/orbchartsUtils'
|
27
|
+
import { d3EventObservable } from '../utils/observables'
|
28
|
+
|
29
|
+
// 建立 grid 主要的 selection
|
30
|
+
export const gridSelectionsObservable = ({ selection, pluginName, clipPathID, seriesLabels$, gridContainerPosition$, gridAxesTransform$, gridGraphicTransform$ }: {
|
31
|
+
selection: d3.Selection<any, unknown, any, unknown>
|
32
|
+
pluginName: string
|
33
|
+
clipPathID: string
|
34
|
+
// computedData$: Observable<ComputedDataGrid>
|
35
|
+
seriesLabels$: Observable<string[]>
|
36
|
+
gridContainerPosition$: Observable<ContainerPositionScaled[]>
|
37
|
+
gridAxesTransform$: Observable<TransformData>
|
38
|
+
gridGraphicTransform$: Observable<TransformData>
|
39
|
+
}) => {
|
40
|
+
const seriesClassName = getClassName(pluginName, 'series')
|
41
|
+
const axesClassName = getClassName(pluginName, 'axes')
|
42
|
+
const graphicClassName = getClassName(pluginName, 'graphic')
|
43
|
+
|
44
|
+
// <g> series selection(container排放位置)
|
45
|
+
// <g> axes selection(旋轉圖軸方向)
|
46
|
+
// <defs> clipPath selection
|
47
|
+
// <g> graphic selection(圖形 scale 範圍的變形)
|
48
|
+
const seriesSelection$ = seriesLabels$.pipe(
|
49
|
+
map((seriesLabels, i) => {
|
50
|
+
return selection
|
51
|
+
.selectAll<SVGGElement, string>(`g.${seriesClassName}`)
|
52
|
+
.data(seriesLabels, d => d)
|
53
|
+
.join(
|
54
|
+
enter => {
|
55
|
+
return enter
|
56
|
+
.append('g')
|
57
|
+
.classed(seriesClassName, true)
|
58
|
+
.each((d, i, g) => {
|
59
|
+
const axesSelection = d3.select(g[i])
|
60
|
+
.selectAll<SVGGElement, ComputedDatumGrid[]>(`g.${axesClassName}`)
|
61
|
+
.data([i])
|
62
|
+
.join(
|
63
|
+
enter => {
|
64
|
+
return enter
|
65
|
+
.append('g')
|
66
|
+
.classed(axesClassName, true)
|
67
|
+
.attr('clip-path', `url(#${clipPathID})`)
|
68
|
+
.each((d, i, g) => {
|
69
|
+
const defsSelection = d3.select(g[i])
|
70
|
+
.selectAll<SVGDefsElement, any>('defs')
|
71
|
+
.data([i])
|
72
|
+
.join('defs')
|
73
|
+
|
74
|
+
const graphicGSelection = d3.select(g[i])
|
75
|
+
.selectAll<SVGGElement, any>('g')
|
76
|
+
.data([i])
|
77
|
+
.join('g')
|
78
|
+
.classed(graphicClassName, true)
|
79
|
+
})
|
80
|
+
},
|
81
|
+
update => update,
|
82
|
+
exit => exit.remove()
|
83
|
+
)
|
84
|
+
})
|
85
|
+
},
|
86
|
+
update => update,
|
87
|
+
exit => exit.remove()
|
88
|
+
)
|
89
|
+
}),
|
90
|
+
shareReplay(1)
|
91
|
+
)
|
92
|
+
|
93
|
+
// <g> series selection
|
94
|
+
combineLatest({
|
95
|
+
seriesSelection: seriesSelection$,
|
96
|
+
gridContainerPosition: gridContainerPosition$
|
97
|
+
}).pipe(
|
98
|
+
switchMap(async d => d)
|
99
|
+
).subscribe(data => {
|
100
|
+
data.seriesSelection
|
101
|
+
.transition()
|
102
|
+
.attr('transform', (d, i) => {
|
103
|
+
const gridContainerPosition = data.gridContainerPosition[i] ?? data.gridContainerPosition[0]
|
104
|
+
const translate = gridContainerPosition.translate
|
105
|
+
const scale = gridContainerPosition.scale
|
106
|
+
return `translate(${translate[0]}, ${translate[1]}) scale(${scale[0]}, ${scale[1]})`
|
107
|
+
})
|
108
|
+
})
|
109
|
+
|
110
|
+
// <g> axes selection
|
111
|
+
const axesSelection$ = combineLatest({
|
112
|
+
seriesSelection: seriesSelection$,
|
113
|
+
gridAxesTransform: gridAxesTransform$
|
114
|
+
}).pipe(
|
115
|
+
switchMap(async d => d),
|
116
|
+
map(data => {
|
117
|
+
return data.seriesSelection
|
118
|
+
.select<SVGGElement>(`g.${axesClassName}`)
|
119
|
+
.style('transform', data.gridAxesTransform.value)
|
120
|
+
}),
|
121
|
+
shareReplay(1)
|
122
|
+
)
|
123
|
+
|
124
|
+
// <defs> clipPath selection
|
125
|
+
const defsSelection$ = axesSelection$.pipe(
|
126
|
+
map(axesSelection => {
|
127
|
+
return axesSelection.select<SVGDefsElement>('defs')
|
128
|
+
}),
|
129
|
+
shareReplay(1)
|
130
|
+
)
|
131
|
+
|
132
|
+
// <g> graphic selection
|
133
|
+
const graphicGSelection$ = combineLatest({
|
134
|
+
axesSelection: axesSelection$,
|
135
|
+
gridGraphicTransform: gridGraphicTransform$
|
136
|
+
}).pipe(
|
137
|
+
switchMap(async d => d),
|
138
|
+
map(data => {
|
139
|
+
const graphicGSelection = data.axesSelection
|
140
|
+
.select<SVGGElement>(`g.${graphicClassName}`)
|
141
|
+
graphicGSelection
|
142
|
+
.transition()
|
143
|
+
.duration(50)
|
144
|
+
.style('transform', data.gridGraphicTransform.value)
|
145
|
+
return graphicGSelection
|
146
|
+
}),
|
147
|
+
shareReplay(1)
|
148
|
+
)
|
149
|
+
|
150
|
+
return {
|
151
|
+
seriesSelection$,
|
152
|
+
axesSelection$,
|
153
|
+
defsSelection$,
|
154
|
+
graphicGSelection$
|
155
|
+
}
|
156
|
+
}
|
157
|
+
|
158
|
+
// 建立 grid 主要的 selection - 只取的container
|
159
|
+
export const gridContainerSelectionsObservable = ({ selection, pluginName, computedData$, gridContainerPosition$, isSeriesSeprate$ }: {
|
160
|
+
selection: d3.Selection<any, unknown, any, unknown>
|
161
|
+
pluginName: string
|
162
|
+
computedData$: Observable<ComputedDataGrid>
|
163
|
+
gridContainerPosition$: Observable<ContainerPositionScaled[]>
|
164
|
+
isSeriesSeprate$: Observable<boolean>
|
165
|
+
}) => {
|
166
|
+
const containerClassName = getClassName(pluginName, 'container')
|
167
|
+
|
168
|
+
const containerSelection$ = combineLatest({
|
169
|
+
computedData: computedData$.pipe(
|
170
|
+
distinctUntilChanged((a, b) => {
|
171
|
+
// 只有當series的數量改變時,才重新計算
|
172
|
+
return a.length === b.length
|
173
|
+
}),
|
174
|
+
),
|
175
|
+
isSeriesSeprate: isSeriesSeprate$
|
176
|
+
}).pipe(
|
177
|
+
switchMap(async (d) => d),
|
178
|
+
map(data => {
|
179
|
+
return data.isSeriesSeprate
|
180
|
+
// series分開的時候顯示各別axis
|
181
|
+
? data.computedData
|
182
|
+
// series合併的時候只顯示第一個axis
|
183
|
+
: [data.computedData[0]]
|
184
|
+
}),
|
185
|
+
map((computedData, i) => {
|
186
|
+
return selection
|
187
|
+
.selectAll<SVGGElement, ComputedDatumGrid[]>(`g.${containerClassName}`)
|
188
|
+
.data(computedData, d => (d && d[0]) ? d[0].seriesIndex : i)
|
189
|
+
.join('g')
|
190
|
+
.classed(containerClassName, true)
|
191
|
+
}),
|
192
|
+
shareReplay(1)
|
193
|
+
)
|
194
|
+
|
195
|
+
combineLatest({
|
196
|
+
containerSelection: containerSelection$,
|
197
|
+
gridContainerPosition: gridContainerPosition$
|
198
|
+
}).pipe(
|
199
|
+
switchMap(async d => d)
|
200
|
+
).subscribe(data => {
|
201
|
+
data.containerSelection
|
202
|
+
.attr('transform', (d, i) => {
|
203
|
+
const gridContainerPosition = data.gridContainerPosition[i] ?? data.gridContainerPosition[0]
|
204
|
+
const translate = gridContainerPosition.translate
|
205
|
+
const scale = gridContainerPosition.scale
|
206
|
+
return `translate(${translate[0]}, ${translate[1]}) scale(${scale[0]}, ${scale[1]})`
|
207
|
+
})
|
208
|
+
// .attr('opacity', 0)
|
209
|
+
// .transition()
|
210
|
+
// .attr('opacity', 1)
|
211
|
+
})
|
212
|
+
|
213
|
+
return containerSelection$
|
214
|
+
}
|
215
|
+
|
216
|
+
// 由事件取得group data的function
|
217
|
+
export const gridGroupPositionFnObservable = ({ fullDataFormatter$, gridAxesSize$, computedData$, fullChartParams$, gridContainerPosition$, layout$ }: {
|
218
|
+
fullDataFormatter$: Observable<DataFormatterGrid>
|
219
|
+
gridAxesSize$: Observable<{
|
220
|
+
width: number;
|
221
|
+
height: number;
|
222
|
+
}>
|
223
|
+
computedData$: Observable<ComputedDataGrid>
|
224
|
+
// GroupDataMap$: Observable<Map<string, ComputedDatumGrid[]>>
|
225
|
+
fullChartParams$: Observable<ChartParams>
|
226
|
+
gridContainerPosition$: Observable<ContainerPositionScaled[]>
|
227
|
+
layout$: Observable<Layout>
|
228
|
+
}): Observable<(event: any) => { groupIndex: number; groupLabel: string }> => {
|
229
|
+
const destroy$ = new Subject()
|
230
|
+
|
231
|
+
// 顯示範圍內的group labels
|
232
|
+
// const scaleRangeGroupLabels$: Observable<string[]> = new Observable(subscriber => {
|
233
|
+
// combineLatest({
|
234
|
+
// dataFormatter: fullDataFormatter$,
|
235
|
+
// computedData: computedData$
|
236
|
+
// }).pipe(
|
237
|
+
// takeUntil(destroy$),
|
238
|
+
// switchMap(async (d) => d),
|
239
|
+
// ).subscribe(data => {
|
240
|
+
// const groupMin = 0
|
241
|
+
// const groupMax = data.computedData[0] ? data.computedData[0].length - 1 : 0
|
242
|
+
// const groupScaleDomainMin = data.dataFormatter.groupAxis.scaleDomain[0] === 'auto'
|
243
|
+
// ? groupMin - data.dataFormatter.groupAxis.scalePadding
|
244
|
+
// : data.dataFormatter.groupAxis.scaleDomain[0] as number - data.dataFormatter.groupAxis.scalePadding
|
245
|
+
// const groupScaleDomainMax = data.dataFormatter.groupAxis.scaleDomain[1] === 'auto'
|
246
|
+
// ? groupMax + data.dataFormatter.groupAxis.scalePadding
|
247
|
+
// : data.dataFormatter.groupAxis.scaleDomain[1] as number + data.dataFormatter.groupAxis.scalePadding
|
248
|
+
|
249
|
+
// // const groupingAmount = data.computedData[0]
|
250
|
+
// // ? data.computedData[0].length
|
251
|
+
// // : 0
|
252
|
+
|
253
|
+
// let _labels = data.dataFormatter.seriesDirection === 'row'
|
254
|
+
// ? (data.computedData[0] ?? []).map(d => d.groupLabel)
|
255
|
+
// : data.computedData.map(d => d[0].groupLabel)
|
256
|
+
|
257
|
+
// const _axisLabels =
|
258
|
+
// // new Array(groupingAmount).fill(0)
|
259
|
+
// // .map((d, i) => {
|
260
|
+
// // return _labels[i] != null
|
261
|
+
// // ? _labels[i]
|
262
|
+
// // : String(i) // 沒有label則用序列號填充
|
263
|
+
// // })
|
264
|
+
// _labels
|
265
|
+
// .filter((d, i) => {
|
266
|
+
// return i >= groupScaleDomainMin && i <= groupScaleDomainMax
|
267
|
+
// })
|
268
|
+
// subscriber.next(_axisLabels)
|
269
|
+
// })
|
270
|
+
// })
|
271
|
+
const groupScaleDomain$ = combineLatest({
|
272
|
+
fullDataFormatter: fullDataFormatter$,
|
273
|
+
gridAxesSize: gridAxesSize$,
|
274
|
+
computedData: computedData$
|
275
|
+
}).pipe(
|
276
|
+
switchMap(async (d) => d),
|
277
|
+
map(data => {
|
278
|
+
const groupMin = 0
|
279
|
+
const groupMax = data.computedData[0] ? data.computedData[0].length - 1 : 0
|
280
|
+
// const groupScaleDomainMin = data.fullDataFormatter.groupAxis.scaleDomain[0] === 'auto'
|
281
|
+
// ? groupMin - data.fullDataFormatter.groupAxis.scalePadding
|
282
|
+
// : data.fullDataFormatter.groupAxis.scaleDomain[0] as number - data.fullDataFormatter.groupAxis.scalePadding
|
283
|
+
const groupScaleDomainMin = data.fullDataFormatter.groupAxis.scaleDomain[0] - data.fullDataFormatter.groupAxis.scalePadding
|
284
|
+
const groupScaleDomainMax = data.fullDataFormatter.groupAxis.scaleDomain[1] === 'max'
|
285
|
+
? groupMax + data.fullDataFormatter.groupAxis.scalePadding
|
286
|
+
: data.fullDataFormatter.groupAxis.scaleDomain[1] as number + data.fullDataFormatter.groupAxis.scalePadding
|
287
|
+
|
288
|
+
return [groupScaleDomainMin, groupScaleDomainMax]
|
289
|
+
}),
|
290
|
+
shareReplay(1)
|
291
|
+
)
|
292
|
+
|
293
|
+
const groupLabels$ = combineLatest({
|
294
|
+
fullDataFormatter: fullDataFormatter$,
|
295
|
+
computedData: computedData$
|
296
|
+
}).pipe(
|
297
|
+
switchMap(async d => d),
|
298
|
+
map(data => {
|
299
|
+
return data.fullDataFormatter.seriesDirection === 'row'
|
300
|
+
? (data.computedData[0] ?? []).map(d => d.groupLabel)
|
301
|
+
: data.computedData.map(d => d[0].groupLabel)
|
302
|
+
})
|
303
|
+
)
|
304
|
+
|
305
|
+
// 顯示範圍內的group labels
|
306
|
+
const scaleRangeGroupLabels$ = combineLatest({
|
307
|
+
groupScaleDomain: groupScaleDomain$,
|
308
|
+
groupLabels: groupLabels$
|
309
|
+
}).pipe(
|
310
|
+
switchMap(async d => d),
|
311
|
+
map(data => {
|
312
|
+
return data.groupLabels
|
313
|
+
.filter((d, i) => {
|
314
|
+
return i >= data.groupScaleDomain[0] && i <= data.groupScaleDomain[1]
|
315
|
+
})
|
316
|
+
})
|
317
|
+
)
|
318
|
+
|
319
|
+
const columnAmount$ = gridContainerPosition$.pipe(
|
320
|
+
map(gridContainerPosition => {
|
321
|
+
const maxColumnIndex = gridContainerPosition.reduce((acc, current) => {
|
322
|
+
return current.columnIndex > acc ? current.columnIndex : acc
|
323
|
+
}, 0)
|
324
|
+
return maxColumnIndex + 1
|
325
|
+
}),
|
326
|
+
distinctUntilChanged()
|
327
|
+
)
|
328
|
+
|
329
|
+
const rowAmount$ = gridContainerPosition$.pipe(
|
330
|
+
map(gridContainerPosition => {
|
331
|
+
const maxRowIndex = gridContainerPosition.reduce((acc, current) => {
|
332
|
+
return current.rowIndex > acc ? current.rowIndex : acc
|
333
|
+
}, 0)
|
334
|
+
return maxRowIndex + 1
|
335
|
+
}),
|
336
|
+
distinctUntilChanged()
|
337
|
+
)
|
338
|
+
|
339
|
+
return new Observable<(event: any) => { groupIndex: number; groupLabel: string }>(subscriber => {
|
340
|
+
combineLatest({
|
341
|
+
dataFormatter: fullDataFormatter$,
|
342
|
+
axisSize: gridAxesSize$,
|
343
|
+
fullChartParams: fullChartParams$,
|
344
|
+
scaleRangeGroupLabels: scaleRangeGroupLabels$,
|
345
|
+
groupLabels: groupLabels$,
|
346
|
+
groupScaleDomain: groupScaleDomain$,
|
347
|
+
columnAmount: columnAmount$,
|
348
|
+
rowAmount: rowAmount$,
|
349
|
+
layout: layout$
|
350
|
+
}).pipe(
|
351
|
+
takeUntil(destroy$),
|
352
|
+
switchMap(async (d) => d),
|
353
|
+
).subscribe(data => {
|
354
|
+
|
355
|
+
const reverse = data.dataFormatter.valueAxis.position === 'right'
|
356
|
+
|| data.dataFormatter.valueAxis.position === 'bottom'
|
357
|
+
? true : false
|
358
|
+
|
359
|
+
// 比例尺座標對應非連續資料索引
|
360
|
+
const xIndexScale = createAxisToLabelIndexScale({
|
361
|
+
axisLabels: data.scaleRangeGroupLabels,
|
362
|
+
axisWidth: data.axisSize.width,
|
363
|
+
padding: data.dataFormatter.groupAxis.scalePadding,
|
364
|
+
reverse
|
365
|
+
})
|
366
|
+
|
367
|
+
// 依比例尺位置計算座標
|
368
|
+
const axisValuePredicate = (event: any) => {
|
369
|
+
return data.dataFormatter.groupAxis.position === 'bottom'
|
370
|
+
|| data.dataFormatter.groupAxis.position === 'top'
|
371
|
+
? event.offsetX - data.fullChartParams.padding.left
|
372
|
+
: event.offsetY - data.fullChartParams.padding.top
|
373
|
+
}
|
374
|
+
|
375
|
+
// 比例尺座標取得groupData的function
|
376
|
+
const createEventGroupData: (event: MouseEvent) => { groupIndex: number; groupLabel: string } = (event: any) => {
|
377
|
+
// 由於event座標是基於底層的,但是container會有多欄,所以要重新計算
|
378
|
+
const eventData = {
|
379
|
+
offsetX: event.offsetX * data.columnAmount % data.layout.rootWidth,
|
380
|
+
offsetY: event.offsetY * data.rowAmount % data.layout.rootHeight
|
381
|
+
}
|
382
|
+
// console.log('data.columnAmount', data.columnAmount, 'data.rowAmount', data.rowAmount, 'data.layout.rootWidth', data.layout.rootWidth, 'data.layout.rootHeight', data.layout.rootHeight)
|
383
|
+
const axisValue = axisValuePredicate(eventData)
|
384
|
+
const xIndex = xIndexScale(axisValue)
|
385
|
+
const currentxIndexStart = Math.ceil(data.groupScaleDomain[0]) // 因為有padding所以會有小數點,所以要無條件進位
|
386
|
+
const groupIndex = xIndex + currentxIndexStart
|
387
|
+
|
388
|
+
return {
|
389
|
+
groupIndex,
|
390
|
+
groupLabel: data.groupLabels[groupIndex] ?? ''
|
391
|
+
}
|
392
|
+
}
|
393
|
+
|
394
|
+
subscriber.next(createEventGroupData)
|
395
|
+
|
396
|
+
return function unsubscribe () {
|
397
|
+
destroy$.next(undefined)
|
398
|
+
}
|
399
|
+
})
|
400
|
+
})
|
401
|
+
}
|
402
|
+
|
403
|
+
export const gridGroupPositionObservable = ({ rootSelection, fullDataFormatter$, containerSize$, gridAxesContainerSize$, computedData$, gridContainerPosition$, layout$ }: {
|
404
|
+
rootSelection: d3.Selection<any, unknown, any, unknown>
|
405
|
+
fullDataFormatter$: Observable<DataFormatterGrid>
|
406
|
+
// gridAxesSize$: Observable<ContainerSize>
|
407
|
+
containerSize$: Observable<ContainerSize>
|
408
|
+
gridAxesContainerSize$: Observable<ContainerSize>
|
409
|
+
computedData$: Observable<ComputedDataGrid>
|
410
|
+
gridContainerPosition$: Observable<ContainerPositionScaled[]>
|
411
|
+
layout$: Observable<Layout>
|
412
|
+
}) => {
|
413
|
+
const rootMousemove$ = d3EventObservable(rootSelection, 'mousemove')
|
414
|
+
|
415
|
+
const groupScaleDomain$ = combineLatest({
|
416
|
+
fullDataFormatter: fullDataFormatter$,
|
417
|
+
// gridAxesSize: gridAxesSize$,
|
418
|
+
computedData: computedData$
|
419
|
+
}).pipe(
|
420
|
+
switchMap(async (d) => d),
|
421
|
+
map(data => {
|
422
|
+
const groupMin = 0
|
423
|
+
const groupMax = data.computedData[0] ? data.computedData[0].length - 1 : 0
|
424
|
+
// const groupScaleDomainMin = data.fullDataFormatter.groupAxis.scaleDomain[0] === 'auto'
|
425
|
+
// ? groupMin - data.fullDataFormatter.groupAxis.scalePadding
|
426
|
+
// : data.fullDataFormatter.groupAxis.scaleDomain[0] as number - data.fullDataFormatter.groupAxis.scalePadding
|
427
|
+
const groupScaleDomainMin = data.fullDataFormatter.groupAxis.scaleDomain[0] - data.fullDataFormatter.groupAxis.scalePadding
|
428
|
+
const groupScaleDomainMax = data.fullDataFormatter.groupAxis.scaleDomain[1] === 'max'
|
429
|
+
? groupMax + data.fullDataFormatter.groupAxis.scalePadding
|
430
|
+
: data.fullDataFormatter.groupAxis.scaleDomain[1] as number + data.fullDataFormatter.groupAxis.scalePadding
|
431
|
+
|
432
|
+
return [groupScaleDomainMin, groupScaleDomainMax]
|
433
|
+
}),
|
434
|
+
shareReplay(1)
|
435
|
+
)
|
436
|
+
|
437
|
+
const groupLabels$ = combineLatest({
|
438
|
+
fullDataFormatter: fullDataFormatter$,
|
439
|
+
computedData: computedData$
|
440
|
+
}).pipe(
|
441
|
+
switchMap(async d => d),
|
442
|
+
map(data => {
|
443
|
+
return data.fullDataFormatter.seriesDirection === 'row'
|
444
|
+
? (data.computedData[0] ?? []).map(d => d.groupLabel)
|
445
|
+
: data.computedData.map(d => d[0].groupLabel)
|
446
|
+
})
|
447
|
+
)
|
448
|
+
|
449
|
+
const scaleRangeGroupLabels$ = combineLatest({
|
450
|
+
groupScaleDomain: groupScaleDomain$,
|
451
|
+
groupLabels: groupLabels$
|
452
|
+
}).pipe(
|
453
|
+
switchMap(async d => d),
|
454
|
+
map(data => {
|
455
|
+
return data.groupLabels
|
456
|
+
.filter((d, i) => {
|
457
|
+
return i >= data.groupScaleDomain[0] && i <= data.groupScaleDomain[1]
|
458
|
+
})
|
459
|
+
})
|
460
|
+
)
|
461
|
+
|
462
|
+
const reverse$ = fullDataFormatter$.pipe(
|
463
|
+
map(d => {
|
464
|
+
return d.valueAxis.position === 'right' || d.valueAxis.position === 'bottom'
|
465
|
+
? true
|
466
|
+
: false
|
467
|
+
})
|
468
|
+
)
|
469
|
+
|
470
|
+
// 比例尺座標對應非連續資料索引
|
471
|
+
const xIndexScale$ = combineLatest({
|
472
|
+
reverse: reverse$,
|
473
|
+
// gridAxesSize: gridAxesSize$,
|
474
|
+
gridAxesContainerSize: gridAxesContainerSize$,
|
475
|
+
scaleRangeGroupLabels: scaleRangeGroupLabels$,
|
476
|
+
fullDataFormatter: fullDataFormatter$
|
477
|
+
}).pipe(
|
478
|
+
switchMap(async d => d),
|
479
|
+
map(data => {
|
480
|
+
return createAxisToLabelIndexScale({
|
481
|
+
axisLabels: data.scaleRangeGroupLabels,
|
482
|
+
axisWidth: data.gridAxesContainerSize.width,
|
483
|
+
padding: data.fullDataFormatter.groupAxis.scalePadding,
|
484
|
+
reverse: data.reverse
|
485
|
+
})
|
486
|
+
})
|
487
|
+
)
|
488
|
+
|
489
|
+
// const columnAmount$ = gridContainerPosition$.pipe(
|
490
|
+
// map(gridContainerPosition => {
|
491
|
+
// const maxColumnIndex = gridContainerPosition.reduce((acc, current) => {
|
492
|
+
// return current.columnIndex > acc ? current.columnIndex : acc
|
493
|
+
// }, 0)
|
494
|
+
// return maxColumnIndex + 1
|
495
|
+
// }),
|
496
|
+
// distinctUntilChanged()
|
497
|
+
// )
|
498
|
+
|
499
|
+
// const rowAmount$ = gridContainerPosition$.pipe(
|
500
|
+
// map(gridContainerPosition => {
|
501
|
+
// const maxRowIndex = gridContainerPosition.reduce((acc, current) => {
|
502
|
+
// return current.rowIndex > acc ? current.rowIndex : acc
|
503
|
+
// }, 0)
|
504
|
+
// return maxRowIndex + 1
|
505
|
+
// }),
|
506
|
+
// distinctUntilChanged()
|
507
|
+
// )
|
508
|
+
|
509
|
+
const axisValue$ = combineLatest({
|
510
|
+
fullDataFormatter: fullDataFormatter$,
|
511
|
+
rootMousemove: rootMousemove$,
|
512
|
+
// containerSize: containerSize$,
|
513
|
+
gridContainerPosition: gridContainerPosition$,
|
514
|
+
// columnAmount: columnAmount$,
|
515
|
+
// rowAmount: rowAmount$,
|
516
|
+
layout: layout$
|
517
|
+
}).pipe(
|
518
|
+
switchMap(async d => d),
|
519
|
+
map(data => {
|
520
|
+
// // 由於event座標是基於底層的,但是container會有多欄,所以要重新計算
|
521
|
+
// const eventData = {
|
522
|
+
// offsetX: data.rootMousemove.offsetX * data.columnAmount % data.layout.rootWidth,
|
523
|
+
// offsetY: data.rootMousemove.offsetY * data.rowAmount % data.layout.rootHeight
|
524
|
+
// }
|
525
|
+
// return data.fullDataFormatter.groupAxis.position === 'bottom'
|
526
|
+
// || data.fullDataFormatter.groupAxis.position === 'top'
|
527
|
+
// ? eventData.offsetX - data.layout.left
|
528
|
+
// : eventData.offsetY - data.layout.top
|
529
|
+
|
530
|
+
if (data.fullDataFormatter.groupAxis.position === 'bottom' || data.fullDataFormatter.groupAxis.position === 'top') {
|
531
|
+
let x = data.rootMousemove.offsetX
|
532
|
+
const rangeArr = data.gridContainerPosition
|
533
|
+
.map((d, i) => [d.translate[0], data.gridContainerPosition[i + 1]?.translate[0] ?? data.layout.rootWidth])
|
534
|
+
.filter(d => d[0] < d[1])
|
535
|
+
const range = rangeArr.find(d => x >= d[0] && x <= d[1])
|
536
|
+
if (range) {
|
537
|
+
x = x - range[0]
|
538
|
+
}
|
539
|
+
return x - data.layout.left
|
540
|
+
} else {
|
541
|
+
let y = data.rootMousemove.offsetY
|
542
|
+
const rangeArr = data.gridContainerPosition
|
543
|
+
.map((d, i) => [d.translate[1], data.gridContainerPosition[i + 1]?.translate[1] ?? data.layout.rootHeight])
|
544
|
+
.filter(d => d[0] < d[1])
|
545
|
+
const range = rangeArr.find(d => y >= d[0] && y <= d[1])
|
546
|
+
if (range) {
|
547
|
+
y = y - range[0]
|
548
|
+
}
|
549
|
+
return y - data.layout.top
|
550
|
+
}
|
551
|
+
})
|
552
|
+
)
|
553
|
+
|
554
|
+
const groupIndex$ = combineLatest({
|
555
|
+
xIndexScale: xIndexScale$,
|
556
|
+
axisValue: axisValue$,
|
557
|
+
groupScaleDomain: groupScaleDomain$
|
558
|
+
}).pipe(
|
559
|
+
switchMap(async d => d),
|
560
|
+
map(data => {
|
561
|
+
const xIndex = data.xIndexScale(data.axisValue)
|
562
|
+
const currentxIndexStart = Math.ceil(data.groupScaleDomain[0]) // 因為有padding所以會有小數點,所以要無條件進位
|
563
|
+
return xIndex + currentxIndexStart
|
564
|
+
})
|
565
|
+
)
|
566
|
+
|
567
|
+
const groupLabel$ = combineLatest({
|
568
|
+
groupIndex: groupIndex$,
|
569
|
+
groupLabels: groupLabels$
|
570
|
+
}).pipe(
|
571
|
+
switchMap(async d => d),
|
572
|
+
map(data => {
|
573
|
+
return data.groupLabels[data.groupIndex] ?? ''
|
574
|
+
})
|
575
|
+
)
|
576
|
+
|
577
|
+
return combineLatest({
|
578
|
+
groupIndex: groupIndex$,
|
579
|
+
groupLabel: groupLabel$
|
580
|
+
}).pipe(
|
581
|
+
switchMap(async d => d),
|
582
|
+
map(data => {
|
583
|
+
return {
|
584
|
+
groupIndex: data.groupIndex,
|
585
|
+
groupLabel: data.groupLabel
|
586
|
+
}
|
587
|
+
})
|
588
|
+
)
|
589
|
+
}
|
590
|
+
|
591
|
+
// const gridContainerEventData$ = ({ eventData$, gridContainerPosition$, layout$ }: {
|
592
|
+
// eventData$: Observable<any>
|
593
|
+
// gridContainerPosition$: Observable<ContainerPositionScaled[]>
|
594
|
+
// layout$: Observable<Layout>
|
595
|
+
// }): Observable<{
|
596
|
+
// offsetX: number;
|
597
|
+
// offsetY: number;
|
598
|
+
// }> => {
|
599
|
+
// const columnAmount$ = gridContainerPosition$.pipe(
|
600
|
+
// map(gridContainerPosition => {
|
601
|
+
// const maxColumnIndex = gridContainerPosition.reduce((acc, current) => {
|
602
|
+
// return current.columnIndex > acc ? current.columnIndex : acc
|
603
|
+
// }, 0)
|
604
|
+
// return maxColumnIndex + 1
|
605
|
+
// }),
|
606
|
+
// distinctUntilChanged()
|
607
|
+
// )
|
608
|
+
|
609
|
+
// const rowAmount$ = gridContainerPosition$.pipe(
|
610
|
+
// map(gridContainerPosition => {
|
611
|
+
// const maxRowIndex = gridContainerPosition.reduce((acc, current) => {
|
612
|
+
// return current.rowIndex > acc ? current.rowIndex : acc
|
613
|
+
// }, 0)
|
614
|
+
// return maxRowIndex + 1
|
615
|
+
// }),
|
616
|
+
// distinctUntilChanged()
|
617
|
+
// )
|
618
|
+
|
619
|
+
// return combineLatest({
|
620
|
+
// eventData: eventData$,
|
621
|
+
// gridContainerPosition: gridContainerPosition$,
|
622
|
+
// layout: layout$,
|
623
|
+
// columnAmount: columnAmount$,
|
624
|
+
// rowAmount: rowAmount$
|
625
|
+
// }).pipe(
|
626
|
+
// switchMap(async d => d),
|
627
|
+
// map(data => {
|
628
|
+
// // 由於event座標是基於底層的,但是container會有多欄,所以要重新計算
|
629
|
+
// const eventData = {
|
630
|
+
// offsetX: data.eventData.offsetX * data.columnAmount % data.layout.rootWidth,
|
631
|
+
// offsetY: data.eventData.offsetY * data.rowAmount % data.layout.rootHeight
|
632
|
+
// }
|
633
|
+
// return eventData
|
634
|
+
// })
|
635
|
+
// )
|
636
636
|
// }
|