@orbcharts/core 3.0.0-beta.1 → 3.0.0-beta.10

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.
Files changed (45) hide show
  1. package/dist/orbcharts-core.es.js +3239 -2854
  2. package/dist/orbcharts-core.umd.js +4 -4
  3. package/dist/src/defaults.d.ts +24 -23
  4. package/dist/src/utils/d3Scale.d.ts +28 -0
  5. package/dist/src/utils/gridObservables.d.ts +29 -19
  6. package/dist/src/utils/index.d.ts +1 -1
  7. package/dist/src/utils/multiGridObservables.d.ts +2 -2
  8. package/dist/src/utils/multiValueObservables.d.ts +73 -0
  9. package/dist/src/utils/orbchartsUtils.d.ts +24 -10
  10. package/dist/src/utils/relationshipObservables.d.ts +13 -0
  11. package/dist/src/utils/seriesObservables.d.ts +4 -4
  12. package/dist/src/utils/treeObservables.d.ts +2 -5
  13. package/package.json +2 -2
  14. package/src/GridChart.ts +2 -2
  15. package/src/MultiGridChart.ts +2 -2
  16. package/src/MultiValueChart.ts +2 -2
  17. package/src/RelationshipChart.ts +2 -2
  18. package/src/SeriesChart.ts +2 -2
  19. package/src/TreeChart.ts +2 -2
  20. package/src/base/createBaseChart.ts +13 -13
  21. package/src/base/validators/chartParamsValidator.ts +6 -6
  22. package/src/defaults.ts +63 -47
  23. package/src/grid/computedDataFn.ts +4 -4
  24. package/src/grid/contextObserverCallback.ts +58 -37
  25. package/src/grid/dataFormatterValidator.ts +14 -14
  26. package/src/multiGrid/computedDataFn.ts +2 -2
  27. package/src/multiValue/computedDataFn.ts +81 -147
  28. package/src/multiValue/contextObserverCallback.ts +150 -2
  29. package/src/relationship/computedDataFn.ts +94 -60
  30. package/src/relationship/contextObserverCallback.ts +68 -0
  31. package/src/tree/computedDataFn.ts +9 -10
  32. package/src/tree/contextObserverCallback.ts +6 -9
  33. package/src/utils/d3Scale.ts +198 -0
  34. package/src/utils/gridObservables.ts +320 -248
  35. package/src/utils/index.ts +1 -1
  36. package/src/utils/multiGridObservables.ts +75 -49
  37. package/src/utils/multiValueObservables.ts +662 -0
  38. package/src/utils/observables.ts +30 -12
  39. package/src/utils/orbchartsUtils.ts +90 -65
  40. package/src/utils/relationshipObservables.ts +85 -0
  41. package/src/utils/seriesObservables.ts +7 -7
  42. package/src/utils/treeObservables.ts +44 -33
  43. package/src/utils/validator.ts +5 -4
  44. package/dist/src/utils/d3Utils.d.ts +0 -19
  45. package/src/utils/d3Utils.ts +0 -108
@@ -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
  }
@@ -1,10 +1,12 @@
1
1
  import type { DataTree, DataTreeObj, DataTreeDatum, ComputedDataFn, ComputedDataTree } from '../../lib/core-types'
2
2
  import { isPlainObject } from '../utils/commonUtils'
3
- import { seriesColorPredicate } from '../utils/orbchartsUtils'
3
+ import { seriesColorPredicate, createDefaultCategoryLabel } from '../utils/orbchartsUtils'
4
4
 
5
5
  export const computedDataFn: ComputedDataFn<'tree'> = (context) => {
6
6
  const { data = [], dataFormatter, chartParams } = context
7
7
 
8
+ const defaultCategoryLabel = createDefaultCategoryLabel()
9
+
8
10
  // <categoryLabel, categoryIndex>
9
11
  const CategoryIndexMap = new Map<string, number>(
10
12
  dataFormatter.categoryLabels.map((label, index) => [label, index])
@@ -15,7 +17,7 @@ export const computedDataFn: ComputedDataFn<'tree'> = (context) => {
15
17
  index: 0,
16
18
  label: '',
17
19
  description: '',
18
- categoryIndex: 0,
20
+ categoryIndex: -1,
19
21
  categoryLabel: '',
20
22
  color: '',
21
23
  visible: true,
@@ -64,7 +66,7 @@ export const computedDataFn: ComputedDataFn<'tree'> = (context) => {
64
66
  data: root.data,
65
67
  // tooltipContent: root.tooltipContent,
66
68
  value: root.value,
67
- categoryLabel: root.categoryLabel,
69
+ categoryLabel: root.categoryLabel ?? defaultCategoryLabel,
68
70
  children: (ChildrenMap.get(root.id) ?? []).map(d => {
69
71
  // 遞迴
70
72
  return createBranchData(d)
@@ -84,14 +86,11 @@ export const computedDataFn: ComputedDataFn<'tree'> = (context) => {
84
86
 
85
87
  const formatBranchData = (branch: DataTreeObj, level: number, seq: number): ComputedDataTree => {
86
88
  const childLayer = level + 1
87
- const categoryLabel: string | null = branch.categoryLabel ?? null
88
- let categoryIndex = 0
89
- if (categoryLabel != null) {
90
- if (!CategoryIndexMap.has(categoryLabel)) {
91
- CategoryIndexMap.set(categoryLabel, CategoryIndexMap.size)
92
- }
93
- categoryIndex = CategoryIndexMap.get(categoryLabel) ?? 0
89
+ const categoryLabel: string = branch.categoryLabel ?? defaultCategoryLabel
90
+ if (!CategoryIndexMap.has(categoryLabel)) {
91
+ CategoryIndexMap.set(categoryLabel, CategoryIndexMap.size)
94
92
  }
93
+ const categoryIndex = CategoryIndexMap.get(categoryLabel) ?? 0
95
94
 
96
95
  const currentIndex = index
97
96
  index++
@@ -3,7 +3,7 @@ import type { ContextObserverCallback } from '../../lib/core-types'
3
3
  import { highlightObservable, categoryDataMapObservable, textSizePxObservable } from '../utils/observables'
4
4
  import {
5
5
  nodeListObservable,
6
- existCategoryLabelsObservable,
6
+ categoryLabelsObservable,
7
7
  treeVisibleComputedDataObservable
8
8
  } from '../utils/treeObservables'
9
9
 
@@ -27,18 +27,15 @@ export const contextObserverCallback: ContextObserverCallback<'tree'> = ({ subje
27
27
  shareReplay(1)
28
28
  )
29
29
 
30
- const existCategoryLabels$ = existCategoryLabelsObservable({
31
- nodeList$,
32
- fullDataFormatter$: observer.fullDataFormatter$
33
- }).pipe(
34
- shareReplay(1)
35
- )
36
-
37
30
  const CategoryDataMap$ = categoryDataMapObservable({
38
31
  datumList$: nodeList$
39
32
  }).pipe(
40
33
  shareReplay(1)
41
34
  )
35
+
36
+ const categoryLabels$ = categoryLabelsObservable(CategoryDataMap$).pipe(
37
+ shareReplay(1)
38
+ )
42
39
 
43
40
  const visibleComputedData$ = treeVisibleComputedDataObservable({
44
41
  computedData$: observer.computedData$
@@ -54,7 +51,7 @@ export const contextObserverCallback: ContextObserverCallback<'tree'> = ({ subje
54
51
  layout$: observer.layout$,
55
52
  textSizePx$,
56
53
  treeHighlight$,
57
- existCategoryLabels$,
54
+ categoryLabels$,
58
55
  CategoryDataMap$,
59
56
  visibleComputedData$
60
57
  }
@@ -0,0 +1,198 @@
1
+ import * as d3 from 'd3'
2
+ import { DEFAULT_DATA_FORMATTER_VALUE_AXIS } from '../defaults'
3
+
4
+ // scaleLinear - 連續資料 -> 座標
5
+ export const createValueToAxisScale = ({
6
+ maxValue = 1,
7
+ minValue = 0,
8
+ axisWidth,
9
+ scaleDomain = DEFAULT_DATA_FORMATTER_VALUE_AXIS.scaleDomain,
10
+ scaleRange = DEFAULT_DATA_FORMATTER_VALUE_AXIS.scaleRange,
11
+ reverse = false
12
+ }: {
13
+ maxValue: number
14
+ minValue: number
15
+ axisWidth: number
16
+ scaleDomain: [number | 'min' | 'auto', number | 'max' | 'auto']
17
+ scaleRange: [number, number] // 0-1
18
+ reverse?: boolean
19
+ }) => {
20
+ // if (minValue === maxValue) {
21
+ // maxValue += 1 // 避免最大及最小值相同造成無法計算scale
22
+ // minValue -= 1
23
+ // }
24
+
25
+ // -- 無值補上預設值 --
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
+
31
+ // -- 'auto' | 'max' | 'min' 替換成實際值 --
32
+ let domainMinValue: number = (() => {
33
+ if (domainMin === 'auto') {
34
+ return minValue < 0 ? minValue : 0
35
+ } else if (domainMin === 'min') {
36
+ return minValue
37
+ } else {
38
+ return domainMin
39
+ }
40
+ })()
41
+
42
+ let domainMaxValue: number = (() => {
43
+ if (domainMax === 'auto') {
44
+ return maxValue >= 0 ? maxValue : 0
45
+ } else if (domainMax === 'max') {
46
+ return maxValue
47
+ } else {
48
+ return domainMax
49
+ }
50
+ })()
51
+ // let rangeMinValue = axisWidth * rangeMin
52
+ // let rangeMaxValue = axisWidth * rangeMax
53
+
54
+ // -- 計算padding --
55
+ // if (padding > 0) {
56
+ // const stepAmount = maxValue - minValue + (padding * 2)
57
+ // const eachStepWidth = axisWidth / stepAmount
58
+ // const paddingWidth = eachStepWidth * padding
59
+ // rangeMinValue += paddingWidth
60
+ // rangeMaxValue -= paddingWidth
61
+ // }
62
+
63
+ // -- 依場景大小換算 --
64
+ const axisDomainMinValue = maxValue - (maxValue - domainMinValue) / (1 - rangeMin)
65
+ const axisDomainMaxValue = domainMaxValue / rangeMax
66
+
67
+ // return d3.scaleLinear()
68
+ // .domain([domainMinValue, domainMaxValue])
69
+ // .range([rangeMinValue, rangeMaxValue])
70
+ if (reverse) {
71
+ return d3.scaleLinear()
72
+ .domain([axisDomainMinValue, axisDomainMaxValue])
73
+ .range([axisWidth, 0])
74
+ } else {
75
+ return d3.scaleLinear()
76
+ .domain([axisDomainMinValue, axisDomainMaxValue])
77
+ .range([0, axisWidth])
78
+ }
79
+ }
80
+
81
+ // scaleLinear - 座標 -> 連續資料
82
+ export const createAxisToValueScale = ({
83
+ maxValue = 1,
84
+ minValue = 0,
85
+ axisWidth,
86
+ scaleDomain = DEFAULT_DATA_FORMATTER_VALUE_AXIS.scaleDomain,
87
+ scaleRange = DEFAULT_DATA_FORMATTER_VALUE_AXIS.scaleRange,
88
+ reverse = false
89
+ }: {
90
+ maxValue: number
91
+ minValue: number
92
+ axisWidth: number
93
+ scaleDomain: [number | 'min' | 'auto', number | 'max' | 'auto']
94
+ scaleRange: [number, number] // 0-1
95
+ reverse?: boolean
96
+ }) => {
97
+ if (minValue === maxValue) {
98
+ maxValue += 1 // 避免最大及最小值相同造成無法計算scale
99
+ minValue -= 1
100
+ }
101
+
102
+ // -- 無值補上預設值 --
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
+
108
+ // -- 'auto' | 'max' | 'min' 替換成實際值 --
109
+ let domainMinValue: number = (() => {
110
+ if (domainMin === 'auto') {
111
+ return minValue < 0 ? minValue : 0
112
+ } else if (domainMin === 'min') {
113
+ return minValue
114
+ } else {
115
+ return domainMin
116
+ }
117
+ })()
118
+
119
+ let domainMaxValue: number = (() => {
120
+ if (domainMax === 'auto') {
121
+ return maxValue >= 0 ? maxValue : 0
122
+ } else if (domainMax === 'max') {
123
+ return maxValue
124
+ } else {
125
+ return domainMax
126
+ }
127
+ })()
128
+ // let rangeMinValue = axisWidth * rangeMin
129
+ // let rangeMaxValue = axisWidth * rangeMax
130
+
131
+ // -- 計算padding --
132
+ // if (padding > 0) {
133
+ // const stepAmount = maxValue - minValue + (padding * 2)
134
+ // const eachStepWidth = axisWidth / stepAmount
135
+ // const paddingWidth = eachStepWidth * padding
136
+ // rangeMinValue += paddingWidth
137
+ // rangeMaxValue -= paddingWidth
138
+ // }
139
+
140
+ // -- 依場景大小換算 --
141
+ const axisDomainMinValue = maxValue - (maxValue - domainMinValue) / (1 - rangeMin)
142
+ const axisDomainMaxValue = domainMaxValue / rangeMax
143
+
144
+ // return d3.scaleLinear()
145
+ // .domain([domainMinValue, domainMaxValue])
146
+ // .range([rangeMinValue, rangeMaxValue])
147
+ if (reverse) {
148
+ return d3.scaleLinear()
149
+ .domain([axisWidth, 0])
150
+ .range([axisDomainMinValue, axisDomainMaxValue])
151
+ } else {
152
+ return d3.scaleLinear()
153
+ .domain([0, axisWidth])
154
+ .range([axisDomainMinValue, axisDomainMaxValue])
155
+ }
156
+ }
157
+
158
+ // scalePoint - 非連續資料 -> 座標
159
+ export const createLabelToAxisScale = ({ axisLabels, axisWidth, padding = 0.5 }: {
160
+ axisLabels: string[]
161
+ axisWidth: number
162
+ padding?: number
163
+ // reverse?: boolean
164
+ }) => {
165
+ let range: [d3.NumberValue, d3.NumberValue] = [0, axisWidth]
166
+
167
+ return d3.scalePoint()
168
+ .domain(axisLabels)
169
+ .range(range)
170
+ .padding(padding)
171
+ }
172
+
173
+ // scaleQuantize - 座標 -> 非連續資料索引
174
+ export const createAxisToLabelIndexScale = ({ axisLabels, axisWidth, padding = 0, reverse = false }:{
175
+ axisLabels: string[] | Date[],
176
+ axisWidth: number
177
+ padding?: number
178
+ reverse?: boolean
179
+ }) => {
180
+
181
+ let range: number[] = axisLabels.map((d: string | Date, i: number) => i)
182
+ if (reverse) {
183
+ range.reverse()
184
+ }
185
+ // if (reverse) {
186
+ // range = axisLabels.map((d: string | Date, i: number) => axisLabels.length - 1 - i)
187
+ // } else {
188
+ // range = axisLabels.map((d: string | Date, i: number) => i)
189
+ // }
190
+ const step = range.length - 1 + (padding * 2) // 圖軸刻度分段數量
191
+ const stepWidth = axisWidth / step
192
+ const rangePadding = stepWidth * padding - (stepWidth * 0.5) // 實際要計算的範圍是圖軸左右那邊增加0.5
193
+
194
+ // console.log('rangePadding', rangePadding)
195
+ return d3.scaleQuantize<number>()
196
+ .domain([rangePadding, axisWidth - rangePadding])
197
+ .range(range)
198
+ }