@orbcharts/core 3.0.0-alpha.21
Sign up to get free protection for your applications and to get access to all the features.
- package/.gitignore +23 -0
- package/LICENSE +201 -0
- package/dist/orbcharts-core.es.js +5096 -0
- package/dist/orbcharts-core.umd.js +3 -0
- package/dist/src/AbstractChart.d.ts +17 -0
- package/dist/src/GridChart.d.ts +6 -0
- package/dist/src/MultiGridChart.d.ts +6 -0
- package/dist/src/MultiValueChart.d.ts +6 -0
- package/dist/src/RelationshipChart.d.ts +6 -0
- package/dist/src/SeriesChart.d.ts +6 -0
- package/dist/src/TreeChart.d.ts +6 -0
- package/dist/src/base/createBaseChart.d.ts +3 -0
- package/dist/src/base/createBasePlugin.d.ts +3 -0
- package/dist/src/defaults.d.ts +37 -0
- package/dist/src/defineGridPlugin.d.ts +1 -0
- package/dist/src/defineMultiGridPlugin.d.ts +1 -0
- package/dist/src/defineMultiValuePlugin.d.ts +1 -0
- package/dist/src/defineNoneDataPlugin.d.ts +1 -0
- package/dist/src/defineRelationshipPlugin.d.ts +1 -0
- package/dist/src/defineSeriesPlugin.d.ts +1 -0
- package/dist/src/defineTreePlugin.d.ts +1 -0
- package/dist/src/grid/computeGridData.d.ts +3 -0
- package/dist/src/grid/createGridContextObserver.d.ts +3 -0
- package/dist/src/grid/gridObservables.d.ts +25 -0
- package/dist/src/index.d.ts +15 -0
- package/dist/src/multiGrid/computeMultiGridData.d.ts +3 -0
- package/dist/src/multiGrid/createMultiGridContextObserver.d.ts +3 -0
- package/dist/src/multiGrid/multiGridObservables.d.ts +0 -0
- package/dist/src/multiValue/computeMultiValueData.d.ts +3 -0
- package/dist/src/multiValue/createMultiValueContextObserver.d.ts +3 -0
- package/dist/src/multiValue/multiValueObservables.d.ts +0 -0
- package/dist/src/relationship/computeRelationshipData.d.ts +3 -0
- package/dist/src/relationship/createRelationshipContextObserver.d.ts +3 -0
- package/dist/src/relationship/relationshipObservables.d.ts +0 -0
- package/dist/src/series/computeSeriesData.d.ts +3 -0
- package/dist/src/series/createSeriesContextObserver.d.ts +3 -0
- package/dist/src/series/seriesObservables.d.ts +8 -0
- package/dist/src/tree/computeTreeData.d.ts +3 -0
- package/dist/src/tree/createTreeContextObserver.d.ts +3 -0
- package/dist/src/tree/treeObservables.d.ts +0 -0
- package/dist/src/types/Axis.d.ts +1 -0
- package/dist/src/types/Chart.d.ts +41 -0
- package/dist/src/types/ChartParams.d.ts +36 -0
- package/dist/src/types/ComputedData.d.ts +28 -0
- package/dist/src/types/ComputedDataGrid.d.ts +10 -0
- package/dist/src/types/ComputedDataMultiGrid.d.ts +3 -0
- package/dist/src/types/ComputedDataMultiValue.d.ts +6 -0
- package/dist/src/types/ComputedDataRelationship.d.ts +18 -0
- package/dist/src/types/ComputedDataSeries.d.ts +6 -0
- package/dist/src/types/ComputedDataTree.d.ts +7 -0
- package/dist/src/types/ContextObserver.d.ts +28 -0
- package/dist/src/types/ContextObserverGrid.d.ts +18 -0
- package/dist/src/types/ContextObserverMultiGrid.d.ts +4 -0
- package/dist/src/types/ContextObserverMultiValue.d.ts +4 -0
- package/dist/src/types/ContextObserverRelationship.d.ts +4 -0
- package/dist/src/types/ContextObserverSeries.d.ts +8 -0
- package/dist/src/types/ContextObserverTree.d.ts +4 -0
- package/dist/src/types/ContextSubject.d.ts +15 -0
- package/dist/src/types/Data.d.ts +19 -0
- package/dist/src/types/DataFormatter.d.ts +40 -0
- package/dist/src/types/DataFormatterGrid.d.ts +20 -0
- package/dist/src/types/DataFormatterMultiGrid.d.ts +16 -0
- package/dist/src/types/DataFormatterMultiValue.d.ts +13 -0
- package/dist/src/types/DataFormatterRelationship.d.ts +5 -0
- package/dist/src/types/DataFormatterSeries.d.ts +10 -0
- package/dist/src/types/DataFormatterTree.d.ts +5 -0
- package/dist/src/types/DataGrid.d.ts +6 -0
- package/dist/src/types/DataMultiGrid.d.ts +6 -0
- package/dist/src/types/DataMultiValue.d.ts +6 -0
- package/dist/src/types/DataRelationship.d.ts +20 -0
- package/dist/src/types/DataSeries.d.ts +6 -0
- package/dist/src/types/DataTree.d.ts +13 -0
- package/dist/src/types/Event.d.ts +64 -0
- package/dist/src/types/Layout.d.ts +8 -0
- package/dist/src/types/Padding.d.ts +6 -0
- package/dist/src/types/Plugin.d.ts +37 -0
- package/dist/src/types/TransformData.d.ts +8 -0
- package/dist/src/types/index.d.ts +37 -0
- package/dist/src/utils/commonUtils.d.ts +8 -0
- package/dist/src/utils/d3Utils.d.ts +25 -0
- package/dist/src/utils/index.d.ts +4 -0
- package/dist/src/utils/observables.d.ts +14 -0
- package/dist/src/utils/orbchartsUtils.d.ts +20 -0
- package/dist/vite.config.d.ts +2 -0
- package/package.json +40 -0
- package/src/AbstractChart.ts +48 -0
- package/src/GridChart.ts +21 -0
- package/src/MultiGridChart.ts +21 -0
- package/src/MultiValueChart.ts +21 -0
- package/src/RelationshipChart.ts +21 -0
- package/src/SeriesChart.ts +21 -0
- package/src/TreeChart.ts +21 -0
- package/src/base/createBaseChart.ts +329 -0
- package/src/base/createBasePlugin.ts +89 -0
- package/src/defaults.ts +229 -0
- package/src/defineGridPlugin.ts +3 -0
- package/src/defineMultiGridPlugin.ts +3 -0
- package/src/defineMultiValuePlugin.ts +3 -0
- package/src/defineNoneDataPlugin.ts +4 -0
- package/src/defineRelationshipPlugin.ts +3 -0
- package/src/defineSeriesPlugin.ts +3 -0
- package/src/defineTreePlugin.ts +3 -0
- package/src/grid/computeGridData.ts +192 -0
- package/src/grid/createGridContextObserver.ts +91 -0
- package/src/grid/gridObservables.ts +359 -0
- package/src/index.ts +21 -0
- package/src/multiGrid/computeMultiGridData.ts +48 -0
- package/src/multiGrid/createMultiGridContextObserver.ts +12 -0
- package/src/multiGrid/multiGridObservables.ts +0 -0
- package/src/multiValue/computeMultiValueData.ts +127 -0
- package/src/multiValue/createMultiValueContextObserver.ts +12 -0
- package/src/multiValue/multiValueObservables.ts +0 -0
- package/src/relationship/computeRelationshipData.ts +101 -0
- package/src/relationship/createRelationshipContextObserver.ts +12 -0
- package/src/relationship/relationshipObservables.ts +0 -0
- package/src/series/computeSeriesData.ts +154 -0
- package/src/series/createSeriesContextObserver.ts +33 -0
- package/src/series/seriesObservables.ts +23 -0
- package/src/tree/computeTreeData.ts +104 -0
- package/src/tree/createTreeContextObserver.ts +12 -0
- package/src/tree/treeObservables.ts +0 -0
- package/src/types/Axis.ts +1 -0
- package/src/types/Chart.ts +46 -0
- package/src/types/ChartParams.ts +50 -0
- package/src/types/ComputedData.ts +66 -0
- package/src/types/ComputedDataGrid.ts +12 -0
- package/src/types/ComputedDataMultiGrid.ts +3 -0
- package/src/types/ComputedDataMultiValue.ts +10 -0
- package/src/types/ComputedDataRelationship.ts +20 -0
- package/src/types/ComputedDataSeries.ts +8 -0
- package/src/types/ComputedDataTree.ts +20 -0
- package/src/types/ContextObserver.ts +38 -0
- package/src/types/ContextObserverGrid.ts +16 -0
- package/src/types/ContextObserverMultiGrid.ts +5 -0
- package/src/types/ContextObserverMultiValue.ts +5 -0
- package/src/types/ContextObserverRelationship.ts +5 -0
- package/src/types/ContextObserverSeries.ts +8 -0
- package/src/types/ContextObserverTree.ts +5 -0
- package/src/types/ContextSubject.ts +18 -0
- package/src/types/Data.ts +45 -0
- package/src/types/DataFormatter.ts +99 -0
- package/src/types/DataFormatterGrid.ts +40 -0
- package/src/types/DataFormatterMultiGrid.ts +23 -0
- package/src/types/DataFormatterMultiValue.ts +19 -0
- package/src/types/DataFormatterRelationship.ts +23 -0
- package/src/types/DataFormatterSeries.ts +26 -0
- package/src/types/DataFormatterTree.ts +10 -0
- package/src/types/DataGrid.ts +11 -0
- package/src/types/DataMultiGrid.ts +7 -0
- package/src/types/DataMultiValue.ts +11 -0
- package/src/types/DataRelationship.ts +27 -0
- package/src/types/DataSeries.ts +11 -0
- package/src/types/DataTree.ts +18 -0
- package/src/types/Event.ts +114 -0
- package/src/types/Layout.ts +12 -0
- package/src/types/Padding.ts +6 -0
- package/src/types/Plugin.ts +60 -0
- package/src/types/TransformData.ts +8 -0
- package/src/types/index.ts +37 -0
- package/src/utils/commonUtils.ts +50 -0
- package/src/utils/d3Utils.ts +87 -0
- package/src/utils/index.ts +4 -0
- package/src/utils/observables.ts +198 -0
- package/src/utils/orbchartsUtils.ts +150 -0
- package/tsconfig.json +14 -0
- package/vite.config.js +45 -0
@@ -0,0 +1,359 @@
|
|
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
|
+
DataFormatterContext,
|
23
|
+
DataFormatterValueAxis,
|
24
|
+
DataFormatterGroupAxis,
|
25
|
+
HighlightTarget,
|
26
|
+
Layout,
|
27
|
+
TransformData } from '../types'
|
28
|
+
import { getMinAndMaxGrid, transposeData } from '../utils/orbchartsUtils'
|
29
|
+
import { createAxisLinearScale, createAxisPointScale, createAxisQuantizeScale } from '../utils/d3Utils'
|
30
|
+
import { highlightObservable } from '../utils/observables'
|
31
|
+
|
32
|
+
export const gridAxesTransformObservable = ({ fullDataFormatter$, layout$ }: {
|
33
|
+
fullDataFormatter$: Observable<DataFormatterTypeMap<'grid'>>
|
34
|
+
layout$: Observable<Layout>
|
35
|
+
}): Observable<TransformData> => {
|
36
|
+
const destroy$ = new Subject()
|
37
|
+
|
38
|
+
function calcAxesTransform ({ xAxis, yAxis, width, height }: {
|
39
|
+
xAxis: DataFormatterGroupAxis | DataFormatterValueAxis,
|
40
|
+
yAxis: DataFormatterValueAxis,
|
41
|
+
width: number,
|
42
|
+
height: number
|
43
|
+
}): TransformData {
|
44
|
+
if (!xAxis || !yAxis) {
|
45
|
+
return {
|
46
|
+
translate: [0, 0],
|
47
|
+
scale: [0, 0],
|
48
|
+
rotate: 0,
|
49
|
+
rotateX: 0,
|
50
|
+
rotateY: 0,
|
51
|
+
value: ''
|
52
|
+
}
|
53
|
+
}
|
54
|
+
// const width = size.width - fullChartParams.layout.left - fullChartParams.layout.right
|
55
|
+
// const height = size.height - fullChartParams.layout.top - fullChartParams.layout.bottom
|
56
|
+
let translateX = 0
|
57
|
+
let translateY = 0
|
58
|
+
let rotate = 0
|
59
|
+
let rotateX = 0
|
60
|
+
let rotateY = 0
|
61
|
+
if (xAxis.position === 'bottom') {
|
62
|
+
if (yAxis.position === 'left') {
|
63
|
+
rotateX = 180
|
64
|
+
translateY = height
|
65
|
+
} else if (yAxis.position === 'right') {
|
66
|
+
rotateX = 180
|
67
|
+
rotateY = 180
|
68
|
+
translateX = width
|
69
|
+
translateY = height
|
70
|
+
} else {
|
71
|
+
// 預設
|
72
|
+
rotateX = 180
|
73
|
+
translateY = height
|
74
|
+
}
|
75
|
+
} else if (xAxis.position === 'top') {
|
76
|
+
if (yAxis.position === 'left') {
|
77
|
+
} else if (yAxis.position === 'right') {
|
78
|
+
rotateY = 180
|
79
|
+
translateX = width
|
80
|
+
} else {
|
81
|
+
// 預設
|
82
|
+
rotateX = 180
|
83
|
+
translateY = height
|
84
|
+
}
|
85
|
+
} else if (xAxis.position === 'left') {
|
86
|
+
if (yAxis.position === 'bottom') {
|
87
|
+
rotate = -90
|
88
|
+
translateY = height
|
89
|
+
} else if (yAxis.position === 'top') {
|
90
|
+
rotate = -90
|
91
|
+
rotateY = 180
|
92
|
+
} else {
|
93
|
+
// 預設
|
94
|
+
rotateX = 180
|
95
|
+
translateY = height
|
96
|
+
}
|
97
|
+
} else if (xAxis.position === 'right') {
|
98
|
+
if (yAxis.position === 'bottom') {
|
99
|
+
rotate = -90
|
100
|
+
rotateX = 180
|
101
|
+
translateY = height
|
102
|
+
translateX = width
|
103
|
+
} else if (yAxis.position === 'top') {
|
104
|
+
rotate = -90
|
105
|
+
translateX = width
|
106
|
+
} else {
|
107
|
+
// 預設
|
108
|
+
rotateX = 180
|
109
|
+
translateY = height
|
110
|
+
}
|
111
|
+
}
|
112
|
+
|
113
|
+
// selection.style('transform', `translate(${translateX}px, ${translateY}px) rotate(${rotate}deg) rotateX(${rotateX}deg) rotateY(${rotateY}deg)`)
|
114
|
+
|
115
|
+
return {
|
116
|
+
translate: [translateX, translateY],
|
117
|
+
scale: [0, 0],
|
118
|
+
rotate,
|
119
|
+
rotateX,
|
120
|
+
rotateY,
|
121
|
+
value: `translate(${translateX}px, ${translateY}px) rotate(${rotate}deg) rotateX(${rotateX}deg) rotateY(${rotateY}deg)`
|
122
|
+
}
|
123
|
+
}
|
124
|
+
|
125
|
+
return new Observable(subscriber => {
|
126
|
+
combineLatest({
|
127
|
+
fullDataFormatter: fullDataFormatter$,
|
128
|
+
layout: layout$
|
129
|
+
}).pipe(
|
130
|
+
takeUntil(destroy$)
|
131
|
+
).subscribe(data => {
|
132
|
+
const axesTransformData = calcAxesTransform({
|
133
|
+
xAxis: data.fullDataFormatter.groupAxis,
|
134
|
+
yAxis: data.fullDataFormatter.valueAxis,
|
135
|
+
width: data.layout.width,
|
136
|
+
height: data.layout.height
|
137
|
+
})
|
138
|
+
|
139
|
+
subscriber.next(axesTransformData)
|
140
|
+
})
|
141
|
+
|
142
|
+
return function unscbscribe () {
|
143
|
+
destroy$.next(undefined)
|
144
|
+
}
|
145
|
+
})
|
146
|
+
}
|
147
|
+
|
148
|
+
export const gridGraphicTransformObservable = ({ computedData$, fullDataFormatter$, layout$ }: {
|
149
|
+
computedData$: Observable<ComputedDataTypeMap<'grid'>>
|
150
|
+
fullDataFormatter$: Observable<DataFormatterTypeMap<'grid'>>
|
151
|
+
layout$: Observable<Layout>
|
152
|
+
}): Observable<TransformData> => {
|
153
|
+
const destroy$ = new Subject()
|
154
|
+
|
155
|
+
function calcGridDataAreaTransform ({ data, groupAxis, valueAxis, width, height }: {
|
156
|
+
data: ComputedDataTypeMap<'grid'>
|
157
|
+
groupAxis: DataFormatterGroupAxis
|
158
|
+
valueAxis: DataFormatterValueAxis
|
159
|
+
width: number
|
160
|
+
height: number
|
161
|
+
}): TransformData {
|
162
|
+
let translateX = 0
|
163
|
+
let translateY = 0
|
164
|
+
let scaleX = 0
|
165
|
+
let scaleY = 0
|
166
|
+
|
167
|
+
// -- groupScale --
|
168
|
+
const groupAxisWidth = (groupAxis.position === 'top' || groupAxis.position === 'bottom')
|
169
|
+
? width
|
170
|
+
: height
|
171
|
+
const groupMin = 0
|
172
|
+
const groupMax = data[0] ? data[0].length - 1 : 0
|
173
|
+
const groupScaleDomainMin = groupAxis.scaleDomain[0] === 'auto'
|
174
|
+
? groupMin - groupAxis.scalePadding
|
175
|
+
: groupAxis.scaleDomain[0] as number - groupAxis.scalePadding
|
176
|
+
const groupScaleDomainMax = groupAxis.scaleDomain[1] === 'auto'
|
177
|
+
? groupMax + groupAxis.scalePadding
|
178
|
+
: groupAxis.scaleDomain[1] as number + groupAxis.scalePadding
|
179
|
+
|
180
|
+
const groupScale: d3.ScaleLinear<number, number> = createAxisLinearScale({
|
181
|
+
maxValue: groupMax,
|
182
|
+
minValue: groupMin,
|
183
|
+
axisWidth: groupAxisWidth,
|
184
|
+
// scaleDomain: groupAxis.scaleDomain,
|
185
|
+
scaleDomain: [groupScaleDomainMin, groupScaleDomainMax],
|
186
|
+
scaleRange: [0, 1]
|
187
|
+
})
|
188
|
+
|
189
|
+
// -- translateX, scaleX --
|
190
|
+
const rangeMinX = groupScale(groupMin)
|
191
|
+
const rangeMaxX = groupScale(groupMax)
|
192
|
+
translateX = rangeMinX
|
193
|
+
const gWidth = rangeMaxX - rangeMinX
|
194
|
+
scaleX = gWidth / groupAxisWidth
|
195
|
+
|
196
|
+
// -- valueScale --
|
197
|
+
const filteredData = data.map((d, i) => {
|
198
|
+
return d.filter((_d, _i) => {
|
199
|
+
return _i >= groupScaleDomainMin && _i <= groupScaleDomainMax && _d.visible == true
|
200
|
+
})
|
201
|
+
})
|
202
|
+
|
203
|
+
const filteredMinAndMax = getMinAndMaxGrid(filteredData)
|
204
|
+
|
205
|
+
const valueAxisWidth = (valueAxis.position === 'left' || valueAxis.position === 'right')
|
206
|
+
? height
|
207
|
+
: width
|
208
|
+
|
209
|
+
const valueScale: d3.ScaleLinear<number, number> = createAxisLinearScale({
|
210
|
+
maxValue: filteredMinAndMax[1],
|
211
|
+
minValue: filteredMinAndMax[0],
|
212
|
+
axisWidth: valueAxisWidth,
|
213
|
+
scaleDomain: valueAxis.scaleDomain,
|
214
|
+
scaleRange: valueAxis.scaleRange
|
215
|
+
})
|
216
|
+
|
217
|
+
// -- translateY, scaleY --
|
218
|
+
const minAndMax = getMinAndMaxGrid(data)
|
219
|
+
const rangeMinY = valueScale(minAndMax[0])
|
220
|
+
const rangeMaxY = valueScale(minAndMax[1])
|
221
|
+
translateY = rangeMinY
|
222
|
+
const gHeight = rangeMaxY - rangeMinY
|
223
|
+
scaleY = gHeight / valueAxisWidth
|
224
|
+
|
225
|
+
return {
|
226
|
+
translate: [translateX, translateY],
|
227
|
+
scale: [scaleX, scaleY],
|
228
|
+
rotate: 0,
|
229
|
+
rotateX: 0,
|
230
|
+
rotateY: 0,
|
231
|
+
value: `translate(${translateX}px, ${translateY}px) scale(${scaleX}, ${scaleY})`
|
232
|
+
}
|
233
|
+
}
|
234
|
+
|
235
|
+
return new Observable(subscriber => {
|
236
|
+
combineLatest({
|
237
|
+
computedData: computedData$,
|
238
|
+
fullDataFormatter: fullDataFormatter$,
|
239
|
+
layout: layout$
|
240
|
+
}).pipe(
|
241
|
+
takeUntil(destroy$)
|
242
|
+
).subscribe(data => {
|
243
|
+
const dataAreaTransformData = calcGridDataAreaTransform ({
|
244
|
+
data: data.computedData,
|
245
|
+
groupAxis: data.fullDataFormatter.groupAxis,
|
246
|
+
valueAxis: data.fullDataFormatter.valueAxis,
|
247
|
+
width: data.layout.width,
|
248
|
+
height: data.layout.height
|
249
|
+
})
|
250
|
+
|
251
|
+
subscriber.next(dataAreaTransformData)
|
252
|
+
})
|
253
|
+
|
254
|
+
return function unscbscribe () {
|
255
|
+
destroy$.next(undefined)
|
256
|
+
}
|
257
|
+
})
|
258
|
+
}
|
259
|
+
|
260
|
+
export const gridAxesOppositeTransformObservable = ({ gridAxesTransform$ }: {
|
261
|
+
gridAxesTransform$: Observable<TransformData>
|
262
|
+
}): Observable<TransformData> => {
|
263
|
+
return gridAxesTransform$.pipe(
|
264
|
+
map(d => {
|
265
|
+
// const translate: [number, number] = [d.translate[0] * -1, d.translate[1] * -1]
|
266
|
+
const translate: [number, number] = [0, 0] // 無需逆轉
|
267
|
+
const scale: [number, number] = [d.scale[0] * -1, d.scale[1] * -1]
|
268
|
+
const rotate = d.rotate * -1
|
269
|
+
const rotateX = d.rotateX * -1
|
270
|
+
const rotateY = d.rotateY * -1
|
271
|
+
return {
|
272
|
+
translate,
|
273
|
+
scale,
|
274
|
+
rotate,
|
275
|
+
rotateX,
|
276
|
+
rotateY,
|
277
|
+
value: `translate(${translate[0]}px, ${translate[1]}px) rotate(${rotate}deg) rotateX(${rotateX}deg) rotateY(${rotateY}deg)`
|
278
|
+
}
|
279
|
+
}),
|
280
|
+
)
|
281
|
+
}
|
282
|
+
|
283
|
+
export const gridAxesSizeObservable = ({ fullDataFormatter$, layout$ }: {
|
284
|
+
fullDataFormatter$: Observable<DataFormatterGrid>
|
285
|
+
layout$: Observable<Layout>
|
286
|
+
}): Observable<{
|
287
|
+
width: number;
|
288
|
+
height: number;
|
289
|
+
}> => {
|
290
|
+
const destroy$ = new Subject()
|
291
|
+
|
292
|
+
function calcAxesSize ({ xAxisPosition, yAxisPosition, width, height }: {
|
293
|
+
xAxisPosition: AxisPosition
|
294
|
+
yAxisPosition: AxisPosition
|
295
|
+
width: number
|
296
|
+
height: number
|
297
|
+
}) {
|
298
|
+
if ((xAxisPosition === 'bottom' || xAxisPosition === 'top') && (yAxisPosition === 'left' || yAxisPosition === 'right')) {
|
299
|
+
return { width, height }
|
300
|
+
} else if ((xAxisPosition === 'left' || xAxisPosition === 'right') && (yAxisPosition === 'bottom' || yAxisPosition === 'top')) {
|
301
|
+
return {
|
302
|
+
width: height,
|
303
|
+
height: width
|
304
|
+
}
|
305
|
+
}
|
306
|
+
}
|
307
|
+
|
308
|
+
return new Observable(subscriber => {
|
309
|
+
combineLatest({
|
310
|
+
fullDataFormatter: fullDataFormatter$,
|
311
|
+
layout: layout$
|
312
|
+
}).pipe(
|
313
|
+
takeUntil(destroy$),
|
314
|
+
// 轉換後會退訂前一個未完成的訂閱事件,因此可以取到「同時間」最後一次的訂閱事件
|
315
|
+
switchMap(async (d) => d),
|
316
|
+
).subscribe(data => {
|
317
|
+
|
318
|
+
const axisSize = calcAxesSize({
|
319
|
+
xAxisPosition: data.fullDataFormatter.groupAxis.position,
|
320
|
+
yAxisPosition: data.fullDataFormatter.valueAxis.position,
|
321
|
+
width: data.layout.width,
|
322
|
+
height: data.layout.height,
|
323
|
+
})
|
324
|
+
|
325
|
+
subscriber.next(axisSize)
|
326
|
+
|
327
|
+
return function unsubscribe () {
|
328
|
+
destroy$.next(undefined)
|
329
|
+
}
|
330
|
+
})
|
331
|
+
})
|
332
|
+
}
|
333
|
+
|
334
|
+
// export const gridHighlightObservable = ({ computedData$, fullChartParams$, event$ }: {
|
335
|
+
// computedData$: Observable<ComputedDataTypeMap<'grid'>>
|
336
|
+
// fullChartParams$: Observable<ChartParams>
|
337
|
+
// event$: Subject<any>
|
338
|
+
// }): Observable<string[]> => {
|
339
|
+
// const datumList$ = computedData$.pipe(
|
340
|
+
// map(d => d.flat())
|
341
|
+
// )
|
342
|
+
// return highlightObservable ({ datumList$, fullChartParams$, event$ })
|
343
|
+
// }
|
344
|
+
|
345
|
+
export const gridVisibleComputedDataObservable = ({ computedData$ }: { computedData$: Observable<ComputedDataTypeMap<'grid'>> }) => {
|
346
|
+
return computedData$.pipe(
|
347
|
+
map(data => {
|
348
|
+
const visibleComputedData = data
|
349
|
+
.map(d => {
|
350
|
+
return d.filter(_d => {
|
351
|
+
return _d.visible == true
|
352
|
+
})
|
353
|
+
})
|
354
|
+
.filter(d => d.length)
|
355
|
+
return visibleComputedData
|
356
|
+
})
|
357
|
+
)
|
358
|
+
}
|
359
|
+
|
package/src/index.ts
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
|
2
|
+
export { SeriesChart } from './SeriesChart'
|
3
|
+
export { GridChart } from './GridChart'
|
4
|
+
export { MultiGridChart } from './MultiGridChart'
|
5
|
+
export { MultiValueChart } from './MultiValueChart'
|
6
|
+
export { RelationshipChart } from './RelationshipChart'
|
7
|
+
export { TreeChart } from './TreeChart'
|
8
|
+
|
9
|
+
export { defineSeriesPlugin } from './defineSeriesPlugin'
|
10
|
+
export { defineGridPlugin } from './defineGridPlugin'
|
11
|
+
export { defineMultiGridPlugin } from './defineMultiGridPlugin'
|
12
|
+
export { defineMultiValuePlugin } from './defineMultiValuePlugin'
|
13
|
+
export { defineNoneDataPlugin } from './defineNoneDataPlugin'
|
14
|
+
export { defineRelationshipPlugin } from './defineRelationshipPlugin'
|
15
|
+
export { defineTreePlugin } from './defineTreePlugin'
|
16
|
+
|
17
|
+
export * from './types'
|
18
|
+
export * from './utils'
|
19
|
+
|
20
|
+
|
21
|
+
|
@@ -0,0 +1,48 @@
|
|
1
|
+
import type { ComputedDataFn } from '../types/ComputedData'
|
2
|
+
import type { DataFormatterGrid } from '../types/DataFormatterGrid'
|
3
|
+
import type { ComputedDataGrid } from '../types/ComputedDataGrid'
|
4
|
+
import { computeGridData } from '../grid/computeGridData'
|
5
|
+
|
6
|
+
export const computeMultiGridData: ComputedDataFn<'multiGrid'> = ({ data = [], dataFormatter, chartParams, layout }) => {
|
7
|
+
if (!data.length) {
|
8
|
+
return []
|
9
|
+
}
|
10
|
+
|
11
|
+
let multiGridData: ComputedDataGrid[] = []
|
12
|
+
|
13
|
+
try {
|
14
|
+
multiGridData = data.map((d, i) => {
|
15
|
+
const dataFormatterGrid: DataFormatterGrid = {
|
16
|
+
...dataFormatter.multiGrid[i],
|
17
|
+
type: `multiGrid_${i}` as any, // 非規範的名稱,用作 datum id 前綴
|
18
|
+
// colors: dataFormatter.colors,
|
19
|
+
colorsPredicate: dataFormatter.multiGrid[i].colorsPredicate,
|
20
|
+
visibleFilter: dataFormatter.visibleFilter as any, // 用any避開function參數型別不同
|
21
|
+
// padding: dataFormatter.padding,
|
22
|
+
tooltipContentFormat: dataFormatter.tooltipContentFormat as any, // 用any避開function參數型別不同
|
23
|
+
}
|
24
|
+
// const layoutGrid: ComputedLayoutBase = {
|
25
|
+
// width: layout.width,
|
26
|
+
// height: layout.height,
|
27
|
+
// top: layout.top,
|
28
|
+
// right: layout.right,
|
29
|
+
// bottom: layout.bottom,
|
30
|
+
// left: layout.left,
|
31
|
+
// rootWidth: layout.rootWidth,
|
32
|
+
// rootHeight: layout.rootHeight,
|
33
|
+
// // content: layout.content[i]
|
34
|
+
// }
|
35
|
+
return computeGridData({
|
36
|
+
data: d,
|
37
|
+
dataFormatter: dataFormatterGrid,
|
38
|
+
chartParams,
|
39
|
+
layout
|
40
|
+
})
|
41
|
+
})
|
42
|
+
} catch (e) {
|
43
|
+
// console.error(e)
|
44
|
+
throw Error(e)
|
45
|
+
}
|
46
|
+
|
47
|
+
return multiGridData
|
48
|
+
}
|
@@ -0,0 +1,12 @@
|
|
1
|
+
import type { ContextObserverFn } from '../types'
|
2
|
+
|
3
|
+
export const createMultiGridContextObserver: ContextObserverFn<'multiGrid'> = ({ subject, observer }) => {
|
4
|
+
|
5
|
+
return {
|
6
|
+
fullParams$: observer.fullParams$,
|
7
|
+
fullChartParams$: observer.fullChartParams$,
|
8
|
+
fullDataFormatter$: observer.fullDataFormatter$,
|
9
|
+
computedData$: observer.computedData$,
|
10
|
+
layout$: observer.layout$,
|
11
|
+
}
|
12
|
+
}
|
File without changes
|
@@ -0,0 +1,127 @@
|
|
1
|
+
import type { DataMultiValue, DataMultiValueDatum } from '../types/DataMultiValue'
|
2
|
+
import type { DataFormatterContext } from '../types/DataFormatter'
|
3
|
+
import type { ComputedDataFn } from '../types/ComputedData'
|
4
|
+
import type { ComputedDataMultiValue, ComputedDatumMultiValue } from '../types/ComputedDataMultiValue'
|
5
|
+
import { formatValueToLabel, createDefaultDatumId } from '../utils/orbchartsUtils'
|
6
|
+
import { createAxisLinearScale, createAxisPointScale } from '../utils/d3Utils'
|
7
|
+
import { getMinAndMaxValue } from '../utils/orbchartsUtils'
|
8
|
+
|
9
|
+
export const computeMultiValueData: ComputedDataFn<'multiValue'> = (context) => {
|
10
|
+
const { data, dataFormatter, chartParams, layout } = context
|
11
|
+
if (!data.length) {
|
12
|
+
return []
|
13
|
+
}
|
14
|
+
|
15
|
+
let computedDataMultiValue: ComputedDatumMultiValue[][] = []
|
16
|
+
|
17
|
+
try {
|
18
|
+
const dataMultiValue: DataMultiValueDatum[][] = data.map((d, i) => {
|
19
|
+
return d.map((_d, _i) => {
|
20
|
+
const datum: DataMultiValueDatum = typeof _d === 'number'
|
21
|
+
? {
|
22
|
+
id: '',
|
23
|
+
label: '',
|
24
|
+
tooltipContent: '',
|
25
|
+
data: {},
|
26
|
+
value: _d
|
27
|
+
}
|
28
|
+
: {
|
29
|
+
id: _d.id ?? '',
|
30
|
+
label: _d.label ?? '',
|
31
|
+
tooltipContent: _d.tooltipContent ?? '',
|
32
|
+
data: _d.data ?? {},
|
33
|
+
value: _d.value
|
34
|
+
}
|
35
|
+
|
36
|
+
return datum
|
37
|
+
})
|
38
|
+
})
|
39
|
+
|
40
|
+
// x軸資料最小及最大值(第二維陣列中的第1筆為x軸資料)
|
41
|
+
const [xMinValue, xMaxValue] = getMinAndMaxValue(dataMultiValue.map(d => d[0]))
|
42
|
+
// y軸資料最小及最大值(第二維陣列中的第2筆為y軸資料)
|
43
|
+
const [yMinValue, yMaxValue] = getMinAndMaxValue(dataMultiValue.map(d => d[1]))
|
44
|
+
|
45
|
+
// const axisWidth = layout.width - dataFormatter.padding.left - dataFormatter.padding.right
|
46
|
+
// const axisHeight = layout.height - dataFormatter.padding.top - dataFormatter.padding.bottom
|
47
|
+
// const axisWidth = layout.width
|
48
|
+
// const axisHeight = layout.height
|
49
|
+
const xAxisWidth = (dataFormatter.xAxis.position === 'top' || dataFormatter.xAxis.position === 'bottom')
|
50
|
+
? layout.width
|
51
|
+
: layout.height
|
52
|
+
const yAxisWidth = (dataFormatter.yAxis.position === 'left' || dataFormatter.yAxis.position === 'right')
|
53
|
+
? layout.height
|
54
|
+
: layout.width
|
55
|
+
|
56
|
+
const xScale: d3.ScaleLinear<number, number> = createAxisLinearScale({
|
57
|
+
maxValue: xMaxValue,
|
58
|
+
minValue: xMinValue,
|
59
|
+
axisWidth: xAxisWidth,
|
60
|
+
// scaleDomain: dataFormatter.xAxis.scaleDomain,
|
61
|
+
// scaleRange: dataFormatter.xAxis.scaleRange
|
62
|
+
scaleDomain: [xMinValue, xMaxValue],
|
63
|
+
scaleRange: [0, 1]
|
64
|
+
})
|
65
|
+
const yScale: d3.ScaleLinear<number, number> = createAxisLinearScale({
|
66
|
+
maxValue: yMaxValue,
|
67
|
+
minValue: yMinValue,
|
68
|
+
axisWidth: yAxisWidth,
|
69
|
+
// scaleDomain: dataFormatter.yAxis.scaleDomain,
|
70
|
+
// scaleRange: dataFormatter.yAxis.scaleRange
|
71
|
+
scaleDomain: [yMinValue, yMaxValue],
|
72
|
+
scaleRange: [0, 1]
|
73
|
+
})
|
74
|
+
|
75
|
+
const _xScaleDoamin: [number, number] = [
|
76
|
+
dataFormatter.xAxis.scaleDomain[0] === 'auto' ? xMinValue : dataFormatter.xAxis.scaleDomain[0],
|
77
|
+
dataFormatter.xAxis.scaleDomain[1] === 'auto' ? xMaxValue : dataFormatter.xAxis.scaleDomain[1]
|
78
|
+
]
|
79
|
+
const _yScaleDoamin: [number, number] = [
|
80
|
+
dataFormatter.yAxis.scaleDomain[0] === 'auto' ? yMinValue : dataFormatter.yAxis.scaleDomain[0],
|
81
|
+
dataFormatter.yAxis.scaleDomain[1] === 'auto' ? yMaxValue : dataFormatter.yAxis.scaleDomain[1]
|
82
|
+
]
|
83
|
+
// 篩選顯示狀態
|
84
|
+
const visibleFilter = (datum: DataMultiValueDatum, rowIndex: number, columnIndex: number, context: DataFormatterContext<"multiValue">) => {
|
85
|
+
// 如果不在scale的範圍內則為false,不再做visibleFilter的判斷
|
86
|
+
if (columnIndex === 0 && datum.value != null && ((datum.value as number) < _xScaleDoamin[0] || datum.value > _xScaleDoamin[1])) {
|
87
|
+
return false
|
88
|
+
}
|
89
|
+
if (columnIndex === 1 && datum.value != null && (datum.value < _yScaleDoamin[0] || datum.value > _yScaleDoamin[1])) {
|
90
|
+
return false
|
91
|
+
}
|
92
|
+
|
93
|
+
return dataFormatter.visibleFilter(datum, rowIndex, columnIndex, context)
|
94
|
+
}
|
95
|
+
|
96
|
+
let index = 0
|
97
|
+
|
98
|
+
computedDataMultiValue = dataMultiValue.map((d, i) => {
|
99
|
+
return d.map((_d, _i) => {
|
100
|
+
const currentIndex = index
|
101
|
+
index++
|
102
|
+
|
103
|
+
const defaultId = createDefaultDatumId(dataFormatter.type, i, _i)
|
104
|
+
// const visible = dataFormatter.visibleFilter(_d, i, _i, context)
|
105
|
+
const visible = visibleFilter(_d, i, _i, context)
|
106
|
+
|
107
|
+
const computedDatum: ComputedDatumMultiValue = {
|
108
|
+
id: _d.id ? _d.id : defaultId,
|
109
|
+
index: currentIndex,
|
110
|
+
label: _d.label ? _d.label : defaultId,
|
111
|
+
tooltipContent: _d.tooltipContent ? _d.tooltipContent : dataFormatter.tooltipContentFormat(_d, i, _i, context),
|
112
|
+
data: _d.data,
|
113
|
+
value: _d.value,
|
114
|
+
// valueLabel: formatValueToLabel(_d.value, dataFormatter.multiValue[_i].valueFormat),
|
115
|
+
axis: _i == 0 ? xScale(_d.value) : yScale(_d.value),
|
116
|
+
visible
|
117
|
+
}
|
118
|
+
return computedDatum
|
119
|
+
})
|
120
|
+
})
|
121
|
+
} catch (e) {
|
122
|
+
// console.error(e)
|
123
|
+
throw Error(e)
|
124
|
+
}
|
125
|
+
|
126
|
+
return computedDataMultiValue
|
127
|
+
}
|
@@ -0,0 +1,12 @@
|
|
1
|
+
import type { ContextObserverFn } from '../types'
|
2
|
+
|
3
|
+
export const createMultiValueContextObserver: ContextObserverFn<'multiValue'> = ({ subject, observer }) => {
|
4
|
+
|
5
|
+
return {
|
6
|
+
fullParams$: observer.fullParams$,
|
7
|
+
fullChartParams$: observer.fullChartParams$,
|
8
|
+
fullDataFormatter$: observer.fullDataFormatter$,
|
9
|
+
computedData$: observer.computedData$,
|
10
|
+
layout$: observer.layout$,
|
11
|
+
}
|
12
|
+
}
|
File without changes
|
@@ -0,0 +1,101 @@
|
|
1
|
+
import type { DataRelationship, DataRelationshipObj, DataRelationshipList, Node, Edge } from '../types/DataRelationship'
|
2
|
+
import type { ComputedDataFn } from '../types/ComputedData'
|
3
|
+
import type { ComputedDataRelationship, ComputedNode, ComputedEdge } from '../types/ComputedDataRelationship'
|
4
|
+
|
5
|
+
export const computeRelationshipData: ComputedDataFn<'relationship'> = (context) => {
|
6
|
+
const { data, dataFormatter, chartParams } = context
|
7
|
+
|
8
|
+
let computedNodes: ComputedNode[] = []
|
9
|
+
let computedEdges: ComputedEdge[] = []
|
10
|
+
|
11
|
+
try {
|
12
|
+
// -- 取得nodes和edges資料 --
|
13
|
+
let nodes: Node[] = []
|
14
|
+
let edges: Edge[] = []
|
15
|
+
if ((data as DataRelationshipObj).nodes) {
|
16
|
+
nodes = (data as DataRelationshipObj).nodes
|
17
|
+
edges = (data as DataRelationshipObj).edges
|
18
|
+
} else if ((data as DataRelationshipList)[0]) {
|
19
|
+
nodes = (data as DataRelationshipList)[0]
|
20
|
+
edges = (data as DataRelationshipList)[1]
|
21
|
+
} else {
|
22
|
+
// 無值直接回傳
|
23
|
+
return {
|
24
|
+
nodes: [],
|
25
|
+
edges: []
|
26
|
+
} as ComputedDataRelationship
|
27
|
+
}
|
28
|
+
|
29
|
+
// -- nodes --
|
30
|
+
computedNodes = nodes.map((node, i) => {
|
31
|
+
return {
|
32
|
+
id: node.id,
|
33
|
+
index: i,
|
34
|
+
label: node.label ?? '',
|
35
|
+
tooltipContent: node.tooltipContent ? node.tooltipContent : dataFormatter.tooltipContentFormat(node, 0, i, context), // 0代表node
|
36
|
+
data: node.data ?? {},
|
37
|
+
value: node.value ?? 0,
|
38
|
+
startNodes: [], // 後面再取得資料
|
39
|
+
startNodeIds: [], // 後面再取得資料
|
40
|
+
endNodes: [], // 後面再取得資料
|
41
|
+
endNodeIds: [], // 後面再取得資料
|
42
|
+
visible: dataFormatter.visibleFilter(node, 0, i, context) // 0代表node
|
43
|
+
}
|
44
|
+
})
|
45
|
+
|
46
|
+
const NodesMap: Map<string, ComputedNode> = new Map(computedNodes.map(d => [d.id, d]))
|
47
|
+
|
48
|
+
// -- edges --
|
49
|
+
computedEdges = edges.map((edge, i) => {
|
50
|
+
return {
|
51
|
+
id: edge.id,
|
52
|
+
index: i,
|
53
|
+
label: edge.label ?? '',
|
54
|
+
tooltipContent: edge.tooltipContent ? edge.tooltipContent : dataFormatter.tooltipContentFormat(edge, 1, i, context), // 1代表edge
|
55
|
+
data: edge.data ?? {},
|
56
|
+
value: edge.value ?? 0,
|
57
|
+
startNode: NodesMap.get(edge.start),
|
58
|
+
startNodeId: edge.start,
|
59
|
+
endNode: NodesMap.get(edge.end),
|
60
|
+
endNodeId: edge.end,
|
61
|
+
visible: dataFormatter.visibleFilter(edge, 1, i, context) // 1代表edge
|
62
|
+
}
|
63
|
+
})
|
64
|
+
|
65
|
+
const StartNodesMap: Map<string, ComputedNode[]> = (function () {
|
66
|
+
const _StartNodesMap = new Map()
|
67
|
+
computedEdges.forEach(edge => {
|
68
|
+
const startNodes: ComputedNode[] = _StartNodesMap.get(edge.endNodeId) ?? []
|
69
|
+
startNodes.push(edge.startNode)
|
70
|
+
_StartNodesMap.set(edge.endNodeId, startNodes)
|
71
|
+
})
|
72
|
+
return _StartNodesMap
|
73
|
+
})()
|
74
|
+
|
75
|
+
const EndNodesMap: Map<string, ComputedNode[]> = (function () {
|
76
|
+
const _EndNodesMap = new Map()
|
77
|
+
computedEdges.forEach(edge => {
|
78
|
+
const endNodes: ComputedNode[] = _EndNodesMap.get(edge.startNodeId) ?? []
|
79
|
+
endNodes.push(edge.endNode)
|
80
|
+
_EndNodesMap.set(edge.startNodeId, endNodes)
|
81
|
+
})
|
82
|
+
return _EndNodesMap
|
83
|
+
})()
|
84
|
+
|
85
|
+
// -- 補齊nodes資料 --
|
86
|
+
Array.from(NodesMap).forEach(([nodeId, node]) => {
|
87
|
+
node.startNodes = StartNodesMap.get(nodeId)
|
88
|
+
node.startNodeIds = node.startNodes.map(d => d.id)
|
89
|
+
node.endNodes = EndNodesMap.get(nodeId)
|
90
|
+
node.endNodeIds = node.endNodes.map(d => d.id)
|
91
|
+
})
|
92
|
+
} catch (e) {
|
93
|
+
// console.error(e)
|
94
|
+
throw Error(e)
|
95
|
+
}
|
96
|
+
|
97
|
+
return {
|
98
|
+
nodes: computedNodes,
|
99
|
+
edges: computedEdges
|
100
|
+
}
|
101
|
+
}
|