@orbcharts/core 3.0.0-beta.4 → 3.0.0-beta.6
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/dist/orbcharts-core.es.js +1549 -1476
- package/dist/orbcharts-core.umd.js +4 -4
- package/dist/src/defaults.d.ts +22 -22
- package/dist/src/utils/orbchartsUtils.d.ts +7 -7
- package/dist/src/utils/relationshipObservables.d.ts +13 -0
- package/package.json +2 -2
- package/src/GridChart.ts +2 -2
- package/src/MultiGridChart.ts +2 -2
- package/src/MultiValueChart.ts +2 -2
- package/src/RelationshipChart.ts +2 -2
- package/src/SeriesChart.ts +2 -2
- package/src/TreeChart.ts +2 -2
- package/src/base/createBaseChart.ts +13 -13
- package/src/base/validators/chartParamsValidator.ts +2 -2
- package/src/defaults.ts +45 -45
- package/src/grid/computedDataFn.ts +4 -4
- package/src/grid/contextObserverCallback.ts +1 -1
- package/src/grid/dataFormatterValidator.ts +10 -10
- package/src/multiGrid/computedDataFn.ts +2 -2
- package/src/relationship/computedDataFn.ts +70 -51
- package/src/relationship/contextObserverCallback.ts +68 -0
- package/src/utils/d3Scale.ts +17 -17
- package/src/utils/gridObservables.ts +40 -28
- package/src/utils/multiGridObservables.ts +7 -7
- package/src/utils/multiValueObservables.ts +46 -27
- package/src/utils/orbchartsUtils.ts +22 -22
- package/src/utils/relationshipObservables.ts +85 -0
- package/src/utils/validator.ts +1 -1
@@ -8,10 +8,13 @@ import type {
|
|
8
8
|
ComputedNode,
|
9
9
|
ComputedEdge
|
10
10
|
} from '../../lib/core-types'
|
11
|
+
import { createDefaultCategoryLabel, seriesColorPredicate } from '../utils/orbchartsUtils'
|
11
12
|
|
12
13
|
export const computedDataFn: ComputedDataFn<'relationship'> = (context) => {
|
13
14
|
const { data, dataFormatter, chartParams } = context
|
14
15
|
|
16
|
+
const defaultCategoryLabel = createDefaultCategoryLabel()
|
17
|
+
|
15
18
|
let computedNodes: ComputedNode[] = []
|
16
19
|
let computedEdges: ComputedEdge[] = []
|
17
20
|
|
@@ -33,86 +36,102 @@ export const computedDataFn: ComputedDataFn<'relationship'> = (context) => {
|
|
33
36
|
} as ComputedDataRelationship
|
34
37
|
}
|
35
38
|
|
39
|
+
const categoryLabels = (() => {
|
40
|
+
// 先使用 dataFormatter.categoryLabels
|
41
|
+
const CategoryLabelsSet = new Set(dataFormatter.categoryLabels)
|
42
|
+
// 再加入 datum 中的 categoryLabel
|
43
|
+
for (let datum of nodes) {
|
44
|
+
const categoryLabel = datum.categoryLabel ?? defaultCategoryLabel
|
45
|
+
CategoryLabelsSet.add(categoryLabel) // 不重覆
|
46
|
+
}
|
47
|
+
for (let datum of edges) {
|
48
|
+
const categoryLabel = datum.categoryLabel ?? defaultCategoryLabel
|
49
|
+
CategoryLabelsSet.add(categoryLabel) // 不重覆
|
50
|
+
}
|
51
|
+
return Array.from(CategoryLabelsSet)
|
52
|
+
})()
|
53
|
+
|
54
|
+
// <categoryLabel, categoryIndex>
|
55
|
+
const CategoryIndexMap = new Map<string, number>(
|
56
|
+
categoryLabels.map((label, index) => [label, index])
|
57
|
+
)
|
58
|
+
|
36
59
|
// -- nodes --
|
37
60
|
computedNodes = nodes.map((node, i) => {
|
61
|
+
const categoryLabel = node.categoryLabel ?? defaultCategoryLabel
|
62
|
+
const categoryIndex = CategoryIndexMap.get(categoryLabel) ?? 0
|
63
|
+
|
38
64
|
const computedNode: ComputedNode = {
|
39
65
|
id: node.id,
|
40
66
|
index: i,
|
41
67
|
label: node.label ?? '',
|
42
68
|
description: node.description ?? '',
|
43
|
-
// tooltipContent: node.tooltipContent ? node.tooltipContent : dataFormatter.tooltipContentFormat(node, 0, i, context), // 0代表node
|
44
69
|
data: node.data ?? {},
|
45
70
|
value: node.value ?? 0,
|
46
|
-
categoryIndex
|
47
|
-
categoryLabel
|
48
|
-
color:
|
49
|
-
startNodes: [], // 後面再取得資料
|
50
|
-
startNodeIds: [], // 後面再取得資料
|
51
|
-
endNodes: [], // 後面再取得資料
|
52
|
-
endNodeIds: [], // 後面再取得資料
|
53
|
-
visible: true //
|
71
|
+
categoryIndex,
|
72
|
+
categoryLabel,
|
73
|
+
color: seriesColorPredicate(categoryIndex, chartParams),
|
74
|
+
// startNodes: [], // 後面再取得資料
|
75
|
+
// startNodeIds: [], // 後面再取得資料
|
76
|
+
// endNodes: [], // 後面再取得資料
|
77
|
+
// endNodeIds: [], // 後面再取得資料
|
78
|
+
visible: true // 先給預設值
|
54
79
|
}
|
80
|
+
|
81
|
+
computedNode.visible = dataFormatter.visibleFilter(computedNode, context)
|
82
|
+
|
55
83
|
return computedNode
|
56
84
|
})
|
57
85
|
|
58
86
|
const NodesMap: Map<string, ComputedNode> = new Map(computedNodes.map(d => [d.id, d]))
|
59
87
|
|
88
|
+
// const StartNodesMap: Map<string, ComputedNode[]> = (function () {
|
89
|
+
// const _StartNodesMap = new Map()
|
90
|
+
// computedEdges.forEach(edge => {
|
91
|
+
// const startNodes: ComputedNode[] = _StartNodesMap.get(edge.endNodeId) ?? []
|
92
|
+
// startNodes.push(edge.startNode)
|
93
|
+
// _StartNodesMap.set(edge.endNodeId, startNodes)
|
94
|
+
// })
|
95
|
+
// return _StartNodesMap
|
96
|
+
// })()
|
97
|
+
|
98
|
+
// const EndNodesMap: Map<string, ComputedNode[]> = (function () {
|
99
|
+
// const _EndNodesMap = new Map()
|
100
|
+
// computedEdges.forEach(edge => {
|
101
|
+
// const endNodes: ComputedNode[] = _EndNodesMap.get(edge.startNodeId) ?? []
|
102
|
+
// endNodes.push(edge.endNode)
|
103
|
+
// _EndNodesMap.set(edge.startNodeId, endNodes)
|
104
|
+
// })
|
105
|
+
// return _EndNodesMap
|
106
|
+
// })()
|
107
|
+
|
60
108
|
// -- edges --
|
61
109
|
computedEdges = edges.map((edge, i) => {
|
110
|
+
const categoryLabel = edge.categoryLabel ?? defaultCategoryLabel
|
111
|
+
const startNode = NodesMap.get(edge.start)
|
112
|
+
const endNode = NodesMap.get(edge.end)
|
113
|
+
|
62
114
|
const computedEdge: ComputedEdge = {
|
63
115
|
id: edge.id,
|
64
116
|
index: i,
|
65
117
|
label: edge.label ?? '',
|
66
118
|
description: edge.description ?? '',
|
67
|
-
// tooltipContent: edge.tooltipContent ? edge.tooltipContent : dataFormatter.tooltipContentFormat(edge, 1, i, context), // 1代表edge
|
68
119
|
data: edge.data ?? {},
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
120
|
+
value: edge.value ?? 0,
|
121
|
+
categoryIndex: CategoryIndexMap.get(categoryLabel),
|
122
|
+
categoryLabel,
|
123
|
+
color: seriesColorPredicate(i, chartParams),
|
124
|
+
startNode,
|
125
|
+
// startNodeId: edge.start,
|
126
|
+
endNode,
|
127
|
+
// endNodeId: edge.end,
|
128
|
+
visible: startNode.visible && endNode.visible
|
75
129
|
}
|
76
130
|
|
77
131
|
return computedEdge
|
78
132
|
})
|
79
133
|
|
80
|
-
const StartNodesMap: Map<string, ComputedNode[]> = (function () {
|
81
|
-
const _StartNodesMap = new Map()
|
82
|
-
computedEdges.forEach(edge => {
|
83
|
-
const startNodes: ComputedNode[] = _StartNodesMap.get(edge.endNodeId) ?? []
|
84
|
-
startNodes.push(edge.startNode)
|
85
|
-
_StartNodesMap.set(edge.endNodeId, startNodes)
|
86
|
-
})
|
87
|
-
return _StartNodesMap
|
88
|
-
})()
|
89
134
|
|
90
|
-
const EndNodesMap: Map<string, ComputedNode[]> = (function () {
|
91
|
-
const _EndNodesMap = new Map()
|
92
|
-
computedEdges.forEach(edge => {
|
93
|
-
const endNodes: ComputedNode[] = _EndNodesMap.get(edge.startNodeId) ?? []
|
94
|
-
endNodes.push(edge.endNode)
|
95
|
-
_EndNodesMap.set(edge.startNodeId, endNodes)
|
96
|
-
})
|
97
|
-
return _EndNodesMap
|
98
|
-
})()
|
99
|
-
|
100
|
-
// -- 補齊nodes資料 --
|
101
|
-
Array.from(NodesMap).forEach(([nodeId, node]) => {
|
102
|
-
node.startNodes = StartNodesMap.get(nodeId)
|
103
|
-
node.startNodeIds = node.startNodes.map(d => d.id)
|
104
|
-
node.endNodes = EndNodesMap.get(nodeId)
|
105
|
-
node.endNodeIds = node.endNodes.map(d => d.id)
|
106
|
-
node.visible = dataFormatter.visibleFilter(node, context)
|
107
|
-
})
|
108
|
-
|
109
|
-
// -- 補齊edges資料 --
|
110
|
-
computedEdges = computedEdges.map(edge => {
|
111
|
-
edge.visible = edge.startNode.visible && edge.endNode.visible
|
112
|
-
? true
|
113
|
-
: false
|
114
|
-
return edge
|
115
|
-
})
|
116
135
|
} catch (e) {
|
117
136
|
// console.error(e)
|
118
137
|
throw Error(e)
|
@@ -1,12 +1,80 @@
|
|
1
|
+
import { map, shareReplay } from 'rxjs'
|
1
2
|
import type { ContextObserverCallback } from '../../lib/core-types'
|
3
|
+
import { highlightObservable, categoryDataMapObservable, textSizePxObservable } from '../utils/observables'
|
4
|
+
import {
|
5
|
+
categoryLabelsObservable,
|
6
|
+
NodeMapObservable,
|
7
|
+
EdgeMapObservable,
|
8
|
+
relationshipVisibleComputedDataObservable
|
9
|
+
} from '../utils/relationshipObservables'
|
2
10
|
|
3
11
|
export const contextObserverCallback: ContextObserverCallback<'relationship'> = ({ subject, observer }) => {
|
4
12
|
|
13
|
+
const textSizePx$ = textSizePxObservable(observer.fullChartParams$).pipe(
|
14
|
+
shareReplay(1)
|
15
|
+
)
|
16
|
+
|
17
|
+
const relationshipHighlightNodes$ = highlightObservable({
|
18
|
+
datumList$: observer.computedData$.pipe(map(data => data.nodes)),
|
19
|
+
fullChartParams$: observer.fullChartParams$,
|
20
|
+
event$: subject.event$
|
21
|
+
}).pipe(
|
22
|
+
shareReplay(1)
|
23
|
+
)
|
24
|
+
|
25
|
+
const relationshipHighlightEdges$ = highlightObservable({
|
26
|
+
datumList$: observer.computedData$.pipe(map(data => data.edges)),
|
27
|
+
fullChartParams$: observer.fullChartParams$,
|
28
|
+
event$: subject.event$
|
29
|
+
}).pipe(
|
30
|
+
shareReplay(1)
|
31
|
+
)
|
32
|
+
|
33
|
+
const CategoryNodeMap$ = categoryDataMapObservable({
|
34
|
+
datumList$: observer.computedData$.pipe(map(data => data.nodes))
|
35
|
+
}).pipe(
|
36
|
+
shareReplay(1)
|
37
|
+
)
|
38
|
+
|
39
|
+
const CategoryEdgeMap$ = categoryDataMapObservable({
|
40
|
+
datumList$: observer.computedData$.pipe(map(data => data.edges))
|
41
|
+
}).pipe(
|
42
|
+
shareReplay(1)
|
43
|
+
)
|
44
|
+
|
45
|
+
const NodeMap$ = NodeMapObservable(observer.computedData$).pipe(
|
46
|
+
shareReplay(1)
|
47
|
+
)
|
48
|
+
|
49
|
+
const EdgeMap$ = EdgeMapObservable(observer.computedData$).pipe(
|
50
|
+
shareReplay(1)
|
51
|
+
)
|
52
|
+
|
53
|
+
const categoryLabels$ = categoryLabelsObservable(CategoryNodeMap$, CategoryEdgeMap$).pipe(
|
54
|
+
shareReplay(1)
|
55
|
+
)
|
56
|
+
|
57
|
+
const visibleComputedData$ = relationshipVisibleComputedDataObservable({
|
58
|
+
computedData$: observer.computedData$,
|
59
|
+
NodeMap$
|
60
|
+
}).pipe(
|
61
|
+
shareReplay(1)
|
62
|
+
)
|
63
|
+
|
5
64
|
return {
|
6
65
|
fullParams$: observer.fullParams$,
|
7
66
|
fullChartParams$: observer.fullChartParams$,
|
8
67
|
fullDataFormatter$: observer.fullDataFormatter$,
|
9
68
|
computedData$: observer.computedData$,
|
10
69
|
layout$: observer.layout$,
|
70
|
+
textSizePx$,
|
71
|
+
relationshipHighlightNodes$,
|
72
|
+
relationshipHighlightEdges$,
|
73
|
+
categoryLabels$,
|
74
|
+
CategoryNodeMap$,
|
75
|
+
CategoryEdgeMap$,
|
76
|
+
NodeMap$,
|
77
|
+
EdgeMap$,
|
78
|
+
visibleComputedData$
|
11
79
|
}
|
12
80
|
}
|
package/src/utils/d3Scale.ts
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
import * as d3 from 'd3'
|
2
|
-
import {
|
2
|
+
import { DEFAULT_DATA_FORMATTER_VALUE_AXIS } from '../defaults'
|
3
3
|
|
4
4
|
// scaleLinear - 連續資料 -> 座標
|
5
5
|
export const createValueToAxisScale = ({
|
6
6
|
maxValue = 1,
|
7
7
|
minValue = 0,
|
8
8
|
axisWidth,
|
9
|
-
scaleDomain =
|
10
|
-
scaleRange =
|
9
|
+
scaleDomain = DEFAULT_DATA_FORMATTER_VALUE_AXIS.scaleDomain,
|
10
|
+
scaleRange = DEFAULT_DATA_FORMATTER_VALUE_AXIS.scaleRange,
|
11
11
|
reverse = false
|
12
12
|
}: {
|
13
13
|
maxValue: number
|
@@ -17,16 +17,16 @@ export const createValueToAxisScale = ({
|
|
17
17
|
scaleRange: [number, number] // 0-1
|
18
18
|
reverse?: boolean
|
19
19
|
}) => {
|
20
|
-
if (minValue === maxValue) {
|
21
|
-
|
22
|
-
|
23
|
-
}
|
20
|
+
// if (minValue === maxValue) {
|
21
|
+
// maxValue += 1 // 避免最大及最小值相同造成無法計算scale
|
22
|
+
// minValue -= 1
|
23
|
+
// }
|
24
24
|
|
25
25
|
// -- 無值補上預設值 --
|
26
|
-
const domainMin: number | 'min' | 'auto' = scaleDomain[0] ??
|
27
|
-
const domainMax: number | 'max' | 'auto' = scaleDomain[1] ??
|
28
|
-
const rangeMin: number = scaleRange[0] ??
|
29
|
-
const rangeMax: number = scaleRange[1] ??
|
26
|
+
const domainMin: number | 'min' | 'auto' = scaleDomain[0] ?? DEFAULT_DATA_FORMATTER_VALUE_AXIS.scaleDomain[0]
|
27
|
+
const domainMax: number | 'max' | 'auto' = scaleDomain[1] ?? DEFAULT_DATA_FORMATTER_VALUE_AXIS.scaleDomain[1]
|
28
|
+
const rangeMin: number = scaleRange[0] ?? DEFAULT_DATA_FORMATTER_VALUE_AXIS.scaleRange[0]
|
29
|
+
const rangeMax: number = scaleRange[1] ?? DEFAULT_DATA_FORMATTER_VALUE_AXIS.scaleRange[1]
|
30
30
|
|
31
31
|
// -- 'auto' | 'max' | 'min' 替換成實際值 --
|
32
32
|
let domainMinValue: number = (() => {
|
@@ -83,8 +83,8 @@ export const createAxisToValueScale = ({
|
|
83
83
|
maxValue = 1,
|
84
84
|
minValue = 0,
|
85
85
|
axisWidth,
|
86
|
-
scaleDomain =
|
87
|
-
scaleRange =
|
86
|
+
scaleDomain = DEFAULT_DATA_FORMATTER_VALUE_AXIS.scaleDomain,
|
87
|
+
scaleRange = DEFAULT_DATA_FORMATTER_VALUE_AXIS.scaleRange,
|
88
88
|
reverse = false
|
89
89
|
}: {
|
90
90
|
maxValue: number
|
@@ -100,10 +100,10 @@ export const createAxisToValueScale = ({
|
|
100
100
|
}
|
101
101
|
|
102
102
|
// -- 無值補上預設值 --
|
103
|
-
const domainMin: number | 'min' | 'auto' = scaleDomain[0] ??
|
104
|
-
const domainMax: number | 'max' | 'auto' = scaleDomain[1] ??
|
105
|
-
const rangeMin: number = scaleRange[0] ??
|
106
|
-
const rangeMax: number = scaleRange[1] ??
|
103
|
+
const domainMin: number | 'min' | 'auto' = scaleDomain[0] ?? DEFAULT_DATA_FORMATTER_VALUE_AXIS.scaleDomain[0]
|
104
|
+
const domainMax: number | 'max' | 'auto' = scaleDomain[1] ?? DEFAULT_DATA_FORMATTER_VALUE_AXIS.scaleDomain[1]
|
105
|
+
const rangeMin: number = scaleRange[0] ?? DEFAULT_DATA_FORMATTER_VALUE_AXIS.scaleRange[0]
|
106
|
+
const rangeMax: number = scaleRange[1] ?? DEFAULT_DATA_FORMATTER_VALUE_AXIS.scaleRange[1]
|
107
107
|
|
108
108
|
// -- 'auto' | 'max' | 'min' 替換成實際值 --
|
109
109
|
let domainMinValue: number = (() => {
|
@@ -30,10 +30,10 @@ import type {
|
|
30
30
|
HighlightTarget,
|
31
31
|
Layout,
|
32
32
|
TransformData } from '../../lib/core-types'
|
33
|
-
import {
|
33
|
+
import { getMinMaxGrid } from './orbchartsUtils'
|
34
34
|
import { createValueToAxisScale, createLabelToAxisScale, createAxisToLabelIndexScale } from './d3Scale'
|
35
35
|
import { calcGridContainerLayout } from './orbchartsUtils'
|
36
|
-
import {
|
36
|
+
import { getMinMaxValue } from './orbchartsUtils'
|
37
37
|
|
38
38
|
export const gridComputedLayoutDataObservable = ({ computedData$, fullDataFormatter$, layout$ }: {
|
39
39
|
computedData$: Observable<ComputedDataTypeMap<'grid'>>
|
@@ -43,7 +43,7 @@ export const gridComputedLayoutDataObservable = ({ computedData$, fullDataFormat
|
|
43
43
|
|
44
44
|
// 未篩選group範圍前的group scale( * 不受到dataFormatter設定影響)
|
45
45
|
function createOriginGroupScale (computedData: ComputedDatumGrid[][], dataFormatter: DataFormatterGrid, layout: Layout) {
|
46
|
-
const groupAxisWidth = (dataFormatter.
|
46
|
+
const groupAxisWidth = (dataFormatter.groupAxis.position === 'top' || dataFormatter.groupAxis.position === 'bottom')
|
47
47
|
? layout.width
|
48
48
|
: layout.height
|
49
49
|
const groupEndIndex = computedData[0] ? computedData[0].length - 1 : 0
|
@@ -60,12 +60,16 @@ export const gridComputedLayoutDataObservable = ({ computedData$, fullDataFormat
|
|
60
60
|
|
61
61
|
// 未篩選group範圍及visible前的value scale( * 不受到dataFormatter設定影響)
|
62
62
|
function createOriginValueScale (computedData: ComputedDatumGrid[][], dataFormatter: DataFormatterGrid, layout: Layout) {
|
63
|
-
const valueAxisWidth = (dataFormatter.
|
63
|
+
const valueAxisWidth = (dataFormatter.valueAxis.position === 'left' || dataFormatter.valueAxis.position === 'right')
|
64
64
|
? layout.height
|
65
65
|
: layout.width
|
66
66
|
|
67
67
|
const listData = computedData.flat()
|
68
|
-
|
68
|
+
let [minValue, maxValue] = getMinMaxValue(listData)
|
69
|
+
if (minValue === maxValue && maxValue === 0) {
|
70
|
+
// 避免最大及最小值相同造成無法計算scale
|
71
|
+
maxValue = 1
|
72
|
+
}
|
69
73
|
|
70
74
|
const valueScale: d3.ScaleLinear<number, number> = createValueToAxisScale({
|
71
75
|
maxValue,
|
@@ -144,8 +148,8 @@ export const gridAxesSizeObservable = ({ fullDataFormatter$, layout$ }: {
|
|
144
148
|
).subscribe(data => {
|
145
149
|
|
146
150
|
const axisSize = calcAxesSize({
|
147
|
-
xAxisPosition: data.fullDataFormatter.
|
148
|
-
yAxisPosition: data.fullDataFormatter.
|
151
|
+
xAxisPosition: data.fullDataFormatter.groupAxis.position,
|
152
|
+
yAxisPosition: data.fullDataFormatter.valueAxis.position,
|
149
153
|
width: data.layout.width,
|
150
154
|
height: data.layout.height,
|
151
155
|
})
|
@@ -230,7 +234,7 @@ export const gridContainerPositionObservable = ({ computedData$, fullDataFormatt
|
|
230
234
|
switchMap(async (d) => d),
|
231
235
|
map(data => {
|
232
236
|
|
233
|
-
if (data.fullDataFormatter.
|
237
|
+
if (data.fullDataFormatter.separateSeries) {
|
234
238
|
// -- 依slotIndexes計算 --
|
235
239
|
return calcGridContainerLayout(data.layout, data.fullDataFormatter.container, data.computedData.length)
|
236
240
|
// return data.computedData.map((seriesData, seriesIndex) => {
|
@@ -319,7 +323,7 @@ export const groupScaleDomainValueObservable = ({ computedData$, fullDataFormatt
|
|
319
323
|
}).pipe(
|
320
324
|
switchMap(async (d) => d),
|
321
325
|
map(data => {
|
322
|
-
const groupAxis = data.fullDataFormatter.
|
326
|
+
const groupAxis = data.fullDataFormatter.groupAxis
|
323
327
|
const groupMin = 0
|
324
328
|
const groupMax = data.computedData[0] ? data.computedData[0].length - 1 : 0
|
325
329
|
// const groupScaleDomainMin = groupAxis.scaleDomain[0] === 'min'
|
@@ -352,11 +356,11 @@ export const filteredMinMaxValueObservable = ({ computedData$, groupScaleDomainV
|
|
352
356
|
})
|
353
357
|
})
|
354
358
|
|
355
|
-
const
|
356
|
-
// if (
|
357
|
-
//
|
359
|
+
const filteredMinMax = getMinMaxGrid(filteredData)
|
360
|
+
// if (filteredMinMax[0] === filteredMinMax[1]) {
|
361
|
+
// filteredMinMax[0] = filteredMinMax[1] - 1 // 避免最大及最小值相同造成無法計算scale
|
358
362
|
// }
|
359
|
-
return
|
363
|
+
return filteredMinMax
|
360
364
|
}),
|
361
365
|
)
|
362
366
|
}
|
@@ -468,8 +472,8 @@ export const gridAxesTransformObservable = ({ fullDataFormatter$, layout$ }: {
|
|
468
472
|
switchMap(async (d) => d),
|
469
473
|
).subscribe(data => {
|
470
474
|
const axesTransformData = calcAxesTransform({
|
471
|
-
xAxis: data.fullDataFormatter.
|
472
|
-
yAxis: data.fullDataFormatter.
|
475
|
+
xAxis: data.fullDataFormatter.groupAxis,
|
476
|
+
yAxis: data.fullDataFormatter.valueAxis,
|
473
477
|
width: data.layout.width,
|
474
478
|
height: data.layout.height
|
475
479
|
})
|
@@ -570,10 +574,11 @@ export const gridGraphicTransformObservable = ({ computedData$, groupScaleDomain
|
|
570
574
|
// })
|
571
575
|
// })
|
572
576
|
|
573
|
-
// const
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
+
// const filteredMinMax = getMinMaxGrid(filteredData)
|
578
|
+
if (filteredMinMaxValue[0] === filteredMinMaxValue[1] && filteredMinMaxValue[1] === 0) {
|
579
|
+
// filteredMinMaxValue[0] = filteredMinMaxValue[1] - 1 // 避免最大及最小值相同造成無法計算scale
|
580
|
+
filteredMinMaxValue[1] = 1 // 避免最大及最小值同等於 0 造成無法計算scale
|
581
|
+
}
|
577
582
|
|
578
583
|
const valueAxisWidth = (valueAxis.position === 'left' || valueAxis.position === 'right')
|
579
584
|
? height
|
@@ -586,15 +591,22 @@ export const gridGraphicTransformObservable = ({ computedData$, groupScaleDomain
|
|
586
591
|
scaleDomain: valueAxis.scaleDomain,
|
587
592
|
scaleRange: valueAxis.scaleRange
|
588
593
|
})
|
589
|
-
|
594
|
+
// console.log({
|
595
|
+
// maxValue: filteredMinMaxValue[1],
|
596
|
+
// minValue: filteredMinMaxValue[0],
|
597
|
+
// axisWidth: valueAxisWidth,
|
598
|
+
// scaleDomain: valueAxis.scaleDomain,
|
599
|
+
// scaleRange: valueAxis.scaleRange
|
600
|
+
// })
|
590
601
|
// -- translateY, scaleY --
|
591
|
-
const
|
592
|
-
if (
|
593
|
-
|
602
|
+
const minMax = getMinMaxGrid(data)
|
603
|
+
if (minMax[0] === minMax[1] && minMax[1] === 0) {
|
604
|
+
// minMax[0] = minMax[1] - 1 // 避免最大及最小值相同造成無法計算scale
|
605
|
+
minMax[1] = 1 // 避免最大及最小值同等於 0 造成無法計算scale
|
594
606
|
}
|
595
|
-
// const rangeMinY = valueScale(
|
596
|
-
const rangeMinY = valueScale(
|
597
|
-
const rangeMaxY = valueScale(
|
607
|
+
// const rangeMinY = valueScale(minMax[0])
|
608
|
+
const rangeMinY = valueScale(minMax[0] > 0 ? 0 : minMax[0]) // * 因為原本的座標就是以 0 到最大值或最小值範範圍計算的,所以這邊也是用同樣的方式計算
|
609
|
+
const rangeMaxY = valueScale(minMax[1] < 0 ? 0 : minMax[1]) // * 因為原本的座標就是以 0 到最大值或最小值範範圍計算的,所以這邊也是用同樣的方式計算
|
598
610
|
translateY = rangeMinY
|
599
611
|
const gHeight = rangeMaxY - rangeMinY
|
600
612
|
scaleY = gHeight / valueAxisWidth
|
@@ -622,8 +634,8 @@ export const gridGraphicTransformObservable = ({ computedData$, groupScaleDomain
|
|
622
634
|
).subscribe(data => {
|
623
635
|
const dataAreaTransformData = calcGridDataAreaTransform ({
|
624
636
|
data: data.computedData,
|
625
|
-
groupAxis: data.fullDataFormatter.
|
626
|
-
valueAxis: data.fullDataFormatter.
|
637
|
+
groupAxis: data.fullDataFormatter.groupAxis,
|
638
|
+
valueAxis: data.fullDataFormatter.valueAxis,
|
627
639
|
groupScaleDomainValue: data.groupScaleDomainValue,
|
628
640
|
filteredMinMaxValue: data.filteredMinMaxValue,
|
629
641
|
width: data.layout.width,
|
@@ -47,7 +47,7 @@ import {
|
|
47
47
|
gridGraphicTransformObservable,
|
48
48
|
gridGraphicReverseScaleObservable,
|
49
49
|
} from './gridObservables'
|
50
|
-
import {
|
50
|
+
import { DEFAULT_DATA_FORMATTER_MULTI_GRID_GRID } from '../defaults'
|
51
51
|
import { calcGridContainerLayout } from './orbchartsUtils'
|
52
52
|
|
53
53
|
// 每一個grid計算出來的所有Observable
|
@@ -95,7 +95,7 @@ export const multiGridEachDetailObservable = ({ fullDataFormatter$, computedData
|
|
95
95
|
// 每次重新計算時,清除之前的訂閱
|
96
96
|
destroy$.next(undefined)
|
97
97
|
|
98
|
-
const defaultGrid = data.fullDataFormatter.gridList[0] ??
|
98
|
+
const defaultGrid = data.fullDataFormatter.gridList[0] ?? DEFAULT_DATA_FORMATTER_MULTI_GRID_GRID
|
99
99
|
|
100
100
|
return data.computedData.map((gridComputedData, gridIndex) => {
|
101
101
|
|
@@ -104,9 +104,9 @@ export const multiGridEachDetailObservable = ({ fullDataFormatter$, computedData
|
|
104
104
|
const gridDataFormatter: DataFormatterGrid = {
|
105
105
|
type: 'grid',
|
106
106
|
visibleFilter: data.fullDataFormatter.visibleFilter as any,
|
107
|
-
grid: {
|
108
|
-
...grid
|
109
|
-
},
|
107
|
+
// grid: {
|
108
|
+
...grid,
|
109
|
+
// },
|
110
110
|
container: {
|
111
111
|
...data.fullDataFormatter.container
|
112
112
|
}
|
@@ -137,7 +137,7 @@ export const multiGridEachDetailObservable = ({ fullDataFormatter$, computedData
|
|
137
137
|
// )
|
138
138
|
|
139
139
|
const isSeriesSeprate$ = gridDataFormatter$.pipe(
|
140
|
-
map(d => d.
|
140
|
+
map(d => d.separateSeries),
|
141
141
|
distinctUntilChanged(),
|
142
142
|
shareReplay(1)
|
143
143
|
)
|
@@ -314,7 +314,7 @@ export const multiGridContainerObservable = ({ computedData$, fullDataFormatter$
|
|
314
314
|
switchMap(async (d) => d),
|
315
315
|
map(data => {
|
316
316
|
|
317
|
-
const defaultGrid = data.fullDataFormatter.gridList[0] ??
|
317
|
+
const defaultGrid = data.fullDataFormatter.gridList[0] ?? DEFAULT_DATA_FORMATTER_MULTI_GRID_GRID
|
318
318
|
const slotAmount = data.computedData.reduce((acc, gridData, gridIndex) => {
|
319
319
|
const grid = data.fullDataFormatter.gridList[gridIndex] ?? defaultGrid
|
320
320
|
const gridSlotAmount = grid.separateSeries
|
@@ -27,7 +27,7 @@ import type {
|
|
27
27
|
HighlightTarget,
|
28
28
|
Layout,
|
29
29
|
TransformData } from '../../lib/core-types'
|
30
|
-
import {
|
30
|
+
import { getMinMax, getMinMaxMultiValue } from './orbchartsUtils'
|
31
31
|
import { createValueToAxisScale, createLabelToAxisScale, createAxisToLabelIndexScale } from './d3Scale'
|
32
32
|
import { calcGridContainerLayout } from './orbchartsUtils'
|
33
33
|
|
@@ -35,8 +35,8 @@ export const minMaxXYObservable = ({ computedData$ }: { computedData$: Observabl
|
|
35
35
|
return computedData$.pipe(
|
36
36
|
map(data => {
|
37
37
|
const flatData = data.flat()
|
38
|
-
const [minX, maxX] =
|
39
|
-
const [minY, maxY] =
|
38
|
+
const [minX, maxX] = getMinMax(flatData.map(d => d.value[0]))
|
39
|
+
const [minY, maxY] = getMinMax(flatData.map(d => d.value[1]))
|
40
40
|
return { minX, maxX, minY, maxY }
|
41
41
|
})
|
42
42
|
)
|
@@ -51,9 +51,15 @@ export const multiValueComputedLayoutDataObservable = ({ computedData$, minMaxXY
|
|
51
51
|
|
52
52
|
// 未篩選範圍前的 scale
|
53
53
|
function createOriginXScale (minMaxXY: { minX: number, maxX: number, minY: number, maxY: number }, layout: Layout) {
|
54
|
+
let maxValue = minMaxXY.maxX
|
55
|
+
let minValue = minMaxXY.minX
|
56
|
+
if (minValue === maxValue && maxValue === 0) {
|
57
|
+
// 避免最大及最小值相同造成無法計算scale
|
58
|
+
maxValue = 1
|
59
|
+
}
|
54
60
|
const valueScale: d3.ScaleLinear<number, number> = createValueToAxisScale({
|
55
|
-
maxValue
|
56
|
-
minValue
|
61
|
+
maxValue,
|
62
|
+
minValue,
|
57
63
|
axisWidth: layout.width,
|
58
64
|
scaleDomain: ['auto', 'auto'], // 不使用dataFormatter設定 --> 以0為基準到最大或最小值為範圍( * 如果是使用[minValue, maxValue]的話,在兩者很接近的情況下有可能造成scale倍率過高而svg變型時失真的情況)
|
59
65
|
scaleRange: [0, 1] // 不使用dataFormatter設定
|
@@ -64,9 +70,15 @@ export const multiValueComputedLayoutDataObservable = ({ computedData$, minMaxXY
|
|
64
70
|
|
65
71
|
// 未篩選範圍及visible前的 scale
|
66
72
|
function createOriginYScale (minMaxXY: { minX: number, maxX: number, minY: number, maxY: number }, layout: Layout) {
|
73
|
+
let maxValue = minMaxXY.maxY
|
74
|
+
let minValue = minMaxXY.minY
|
75
|
+
if (minValue === maxValue && maxValue === 0) {
|
76
|
+
// 避免最大及最小值相同造成無法計算scale
|
77
|
+
maxValue = 1
|
78
|
+
}
|
67
79
|
const valueScale: d3.ScaleLinear<number, number> = createValueToAxisScale({
|
68
|
-
maxValue
|
69
|
-
minValue
|
80
|
+
maxValue,
|
81
|
+
minValue,
|
70
82
|
axisWidth: layout.height,
|
71
83
|
scaleDomain: ['auto', 'auto'], // 不使用dataFormatter設定 --> 以0為基準到最大或最小值為範圍( * 如果是使用[minValue, maxValue]的話,在兩者很接近的情況下有可能造成scale倍率過高而svg變型時失真的情況)
|
72
84
|
scaleRange: [0, 1], // 不使用dataFormatter設定
|
@@ -402,7 +414,7 @@ export const filteredMinMaxXYDataObservable = ({ visibleComputedLayoutData$, min
|
|
402
414
|
let maxYDatum: ComputedLayoutDatumMultiValue | null = null
|
403
415
|
// console.log('data.visibleComputedLayoutData', data.visibleComputedLayoutData)
|
404
416
|
// minX, maxX, minY, maxY 範圍內的最大最小值資料
|
405
|
-
|
417
|
+
// console.log({ minX, maxX, minY, maxY })
|
406
418
|
for (let categoryData of data.visibleComputedLayoutData) {
|
407
419
|
for (let datum of categoryData) {
|
408
420
|
// 比較矩形範圍(所以 minX, maxX, minY, maxY 要同時比較)
|
@@ -471,7 +483,7 @@ export const multiValueGraphicTransformObservable = ({ minMaxXY$, filteredMinMax
|
|
471
483
|
// // minX, maxX, filteredMinX, filteredMaxX
|
472
484
|
// let filteredMinX = 0
|
473
485
|
// let filteredMaxX = 0
|
474
|
-
// let [minX, maxX] =
|
486
|
+
// let [minX, maxX] = getMinMax(flatData.map(d => d.value[0]))
|
475
487
|
// if (minX === maxX) {
|
476
488
|
// minX = maxX - 1 // 避免最大及最小值相同造成無法計算scale
|
477
489
|
// }
|
@@ -496,7 +508,7 @@ export const multiValueGraphicTransformObservable = ({ minMaxXY$, filteredMinMax
|
|
496
508
|
// // minY, maxY, filteredMinY, filteredMaxY
|
497
509
|
// let filteredMinY = 0
|
498
510
|
// let filteredMaxY = 0
|
499
|
-
// let [minY, maxY] =
|
511
|
+
// let [minY, maxY] = getMinMax(flatData.map(d => d.value[1]))
|
500
512
|
// console.log('filteredMinMaxXYData', filteredMinMaxXYData)
|
501
513
|
let { minX, maxX, minY, maxY } = minMaxXY
|
502
514
|
// console.log({ minX, maxX, minY, maxY })
|
@@ -505,14 +517,6 @@ export const multiValueGraphicTransformObservable = ({ minMaxXY$, filteredMinMax
|
|
505
517
|
let filteredMinY = filteredMinMaxXYData.minYDatum.value[1] ?? 0
|
506
518
|
let filteredMaxY = filteredMinMaxXYData.maxYDatum.value[1] ?? 0
|
507
519
|
|
508
|
-
if (minX === maxX) {
|
509
|
-
maxX += 1 // 避免最大及最小值相同造成無法計算scale
|
510
|
-
minX -= 1
|
511
|
-
}
|
512
|
-
if (minY === maxY) {
|
513
|
-
maxY += 1 // 避免最大及最小值相同造成無法計算scale
|
514
|
-
minY -= 1
|
515
|
-
}
|
516
520
|
// if (yAxis.scaleDomain[0] === 'auto' && filteredMinY > 0) {
|
517
521
|
// filteredMinY = 0
|
518
522
|
// } else if (typeof yAxis.scaleDomain[0] === 'number') {
|
@@ -527,15 +531,24 @@ export const multiValueGraphicTransformObservable = ({ minMaxXY$, filteredMinMax
|
|
527
531
|
// } else {
|
528
532
|
// filteredMaxY = maxY
|
529
533
|
// }
|
530
|
-
|
531
|
-
// filteredMaxX += 1 // 避免最大及最小值相同造成無法計算scale
|
532
|
-
// filteredMinX -= 1
|
533
|
-
// }
|
534
|
-
// if (filteredMinY === filteredMaxY) {
|
535
|
-
// filteredMaxY += 1 // 避免最大及最小值相同造成無法計算scale
|
536
|
-
// filteredMinY -= 1
|
537
|
-
// }
|
534
|
+
|
538
535
|
// console.log({ minX, maxX, minY, maxY, filteredMinX, filteredMaxX, filteredMinY, filteredMaxY })
|
536
|
+
if (filteredMinX === filteredMaxX && filteredMaxX === 0) {
|
537
|
+
// 避免最大及最小值相同造成無法計算scale
|
538
|
+
filteredMaxX = 1
|
539
|
+
}
|
540
|
+
if (filteredMinY === filteredMaxY && filteredMaxY === 0) {
|
541
|
+
// 避免最大及最小值相同造成無法計算scale
|
542
|
+
filteredMaxY = 1
|
543
|
+
}
|
544
|
+
if (minX === maxX && maxX === 0) {
|
545
|
+
// 避免最大及最小值相同造成無法計算scale
|
546
|
+
maxX = 1
|
547
|
+
}
|
548
|
+
if (minY === maxY && maxY === 0) {
|
549
|
+
// 避免最大及最小值相同造成無法計算scale
|
550
|
+
maxY = 1
|
551
|
+
}
|
539
552
|
// -- xScale --
|
540
553
|
const xScale: d3.ScaleLinear<number, number> = createValueToAxisScale({
|
541
554
|
maxValue: filteredMaxX,
|
@@ -568,7 +581,6 @@ export const multiValueGraphicTransformObservable = ({ minMaxXY$, filteredMinMax
|
|
568
581
|
translateY = rangeMaxY // 最大值的 y 最小(最上方)
|
569
582
|
const gHeight = rangeMinY - rangeMaxY // 最大的 y 減最小的 y
|
570
583
|
scaleY = gHeight / height
|
571
|
-
// console.log({ gHeight, height, rangeMaxY, rangeMinY, scaleY, translateY })
|
572
584
|
|
573
585
|
return {
|
574
586
|
translate: [translateX, translateY],
|
@@ -590,6 +602,13 @@ export const multiValueGraphicTransformObservable = ({ minMaxXY$, filteredMinMax
|
|
590
602
|
takeUntil(destroy$),
|
591
603
|
switchMap(async (d) => d),
|
592
604
|
).subscribe(data => {
|
605
|
+
if (!data.filteredMinMaxXYData.minXDatum || !data.filteredMinMaxXYData.maxXDatum
|
606
|
+
|| data.filteredMinMaxXYData.minXDatum.value[0] == null || data.filteredMinMaxXYData.maxXDatum.value[0] == null
|
607
|
+
|| !data.filteredMinMaxXYData.minYDatum || !data.filteredMinMaxXYData.maxYDatum
|
608
|
+
|| data.filteredMinMaxXYData.minYDatum.value[1] == null || data.filteredMinMaxXYData.maxYDatum.value[1] == null
|
609
|
+
) {
|
610
|
+
return
|
611
|
+
}
|
593
612
|
const dataAreaTransformData = calcDataAreaTransform({
|
594
613
|
minMaxXY: data.minMaxXY,
|
595
614
|
filteredMinMaxXYData: data.filteredMinMaxXYData,
|