@orbcharts/core 3.0.0-beta.3 → 3.0.0-beta.5

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.
@@ -13,11 +13,12 @@ import type {
13
13
  DataMultiValue,
14
14
  DataMultiValueDatum,
15
15
  DataMultiValueValue,
16
+ ComputedLayoutDatumMultiValue,
16
17
  DataFormatterContainer,
17
18
  SeriesDirection,
18
19
  DataFormatterGridGrid,
19
- SeriesContainerPosition,
20
- GridContainerPosition,
20
+ ContainerPosition,
21
+ ContainerPositionScaled,
21
22
  Layout
22
23
  } from '../../lib/core-types'
23
24
  import { isPlainObject } from './commonUtils'
@@ -29,8 +30,11 @@ export function formatValueToLabel (value: any, valueFormatter: string | ((text:
29
30
  return d3.format(valueFormatter as string)!(value)
30
31
  }
31
32
 
32
- export function createDefaultDatumId (chartTypeOrPrefix: string, levelOneIndex: number, levelTwoIndex: number, levelThreeIndex?: number) {
33
- let text = `${chartTypeOrPrefix}_${levelOneIndex}_${levelTwoIndex}`
33
+ export function createDefaultDatumId (chartTypeOrPrefix: string, levelOneIndex: number, levelTwoIndex?: number, levelThreeIndex?: number) {
34
+ let text = `${chartTypeOrPrefix}_${levelOneIndex}`
35
+ if (levelTwoIndex != null) {
36
+ text += `_${levelTwoIndex}`
37
+ }
34
38
  if (levelThreeIndex != null) {
35
39
  text += `_${levelThreeIndex}`
36
40
  }
@@ -45,6 +49,11 @@ export function createDefaultGroupLabel (chartTypeOrPrefix: string, groupIndex:
45
49
  return `${chartTypeOrPrefix}_group${groupIndex}`
46
50
  }
47
51
 
52
+ export function createDefaultCategoryLabel () {
53
+ // return `${chartTypeOrPrefix}_category`
54
+ return '' // 空值
55
+ }
56
+
48
57
  export function createGridSeriesLabels ({ transposedDataGrid, dataFormatterGrid, chartType = 'grid' }: {
49
58
  transposedDataGrid: DataGridDatum[][],
50
59
  dataFormatterGrid: DataFormatterGridGrid
@@ -167,14 +176,63 @@ export function getMinAndMaxMultiGrid (data: DataMultiGrid): [number, number] {
167
176
  }
168
177
 
169
178
  // 取得最小及最大值 - MultiValue Data
170
- export function getMinAndMaxMultiValue (data: DataMultiValue, valueIndex: number = 2): [number, number] {
171
- const flatData: (DataMultiValueDatum | DataMultiValueValue)[] = data.flat().filter((d, i) => i == valueIndex)
172
- const arr = flatData
173
- .filter(d => (d == null || (isPlainObject(d) && (d as DataMultiValueDatum).value == null)) === false) // 過濾掉null
174
- .map(d => typeof d === 'number' ? d : d.value )
179
+ export function getMinAndMaxMultiValue (data: DataMultiValue, valueIndex: number): [number, number] {
180
+ const arr: number[] = data
181
+ .map(d => {
182
+ if (Array.isArray(d)) {
183
+ return d[valueIndex] ?? null
184
+ } else if (isPlainObject(d)) {
185
+ return (d as DataMultiValueDatum).value[valueIndex] ?? null
186
+ } else {
187
+ return null
188
+ }
189
+ })
190
+ .filter(d => d != null)
175
191
  return getMinAndMax(arr)
176
192
  }
177
193
 
194
+ export function getMinAndMaxMultiValueXY ({ data, minX, maxX, minY, maxY }: {
195
+ data: ComputedLayoutDatumMultiValue[][]
196
+ minX: number
197
+ maxX: number
198
+ minY: number
199
+ maxY: number
200
+ }) {
201
+ let filteredData: ComputedLayoutDatumMultiValue[][] = []
202
+ let minXDatum: ComputedLayoutDatumMultiValue | null = null
203
+ let maxXDatum: ComputedLayoutDatumMultiValue | null = null
204
+ let minYDatum: ComputedLayoutDatumMultiValue | null = null
205
+ let maxYDatum: ComputedLayoutDatumMultiValue | null = null
206
+
207
+ for (let categoryData of data) {
208
+ for (let datum of categoryData) {
209
+ if (datum.axisX >= minX && datum.axisX <= maxX && datum.axisY >= minY && datum.axisY <= maxY) {
210
+ filteredData.push(categoryData)
211
+ if (minXDatum == null || datum.axisX < minXDatum.axisX) {
212
+ minXDatum = datum
213
+ }
214
+ if (maxXDatum == null || datum.axisX > maxXDatum.axisX) {
215
+ maxXDatum = datum
216
+ }
217
+ if (minYDatum == null || datum.axisY < minYDatum.axisY) {
218
+ minYDatum = datum
219
+ }
220
+ if (maxYDatum == null || datum.axisY > maxYDatum.axisY) {
221
+ maxYDatum = datum
222
+ }
223
+ }
224
+ }
225
+ }
226
+
227
+ return {
228
+ minXDatum,
229
+ maxXDatum,
230
+ minYDatum,
231
+ maxYDatum,
232
+ filteredData
233
+ }
234
+ }
235
+
178
236
  // @Q@ 待處理
179
237
  // // 取得最小及最大值 - Relationship Data
180
238
  // export function getMinAndMaxRelationship (data: DataRelationship, target: 'nodes' | 'edges' = 'nodes'): [number, number] {
@@ -220,24 +278,6 @@ export function seriesColorPredicate (seriesIndex: number, chartParams: ChartPar
220
278
  ]
221
279
  }
222
280
 
223
- // export function calcSeriesContainerPosition (layout: Layout, container: DataFormatterContainer, rowIndex: number, columnIndex: number) {
224
- // const { gap, rowAmount, columnAmount } = container
225
- // const width = (layout.width - (gap * (columnAmount - 1))) / columnAmount
226
- // const height = (layout.height - (gap * (rowAmount - 1))) / rowAmount
227
- // const x = columnIndex * width + (columnIndex * gap)
228
- // const y = rowIndex * height + (rowIndex * gap)
229
- // // const translate: [number, number] = [x, y]
230
-
231
- // return {
232
- // // translate,
233
- // startX: x,
234
- // startY: y,
235
- // centerX: x + width / 2,
236
- // centerY: y + height / 2,
237
- // width,
238
- // height
239
- // }
240
- // }
241
281
 
242
282
  // 計算預設欄列數量
243
283
  // 規則1.rowAmount*columnAmount要大於或等於amount,並且數字要盡可能小
@@ -251,7 +291,7 @@ function calcGridDimensions (amount: number): { rowAmount: number; columnAmount:
251
291
  return { rowAmount, columnAmount }
252
292
  }
253
293
 
254
- export function calcSeriesContainerLayout (layout: Layout, container: DataFormatterContainer, amount: number): SeriesContainerPosition[] {
294
+ export function calcSeriesContainerLayout (layout: Layout, container: DataFormatterContainer, amount: number): ContainerPosition[] {
255
295
  const { gap } = container
256
296
  const { rowAmount, columnAmount } = (container.rowAmount * container.columnAmount) >= amount
257
297
  // 如果container設定的rowAmount和columnAmount的乘積大於或等於amount,則使用目前設定
@@ -284,22 +324,7 @@ export function calcSeriesContainerLayout (layout: Layout, container: DataFormat
284
324
  })
285
325
  }
286
326
 
287
- // export function calcGridContainerPosition (layout: Layout, container: DataFormatterContainer, rowIndex: number, columnIndex: number) {
288
- // const { gap, rowAmount, columnAmount } = container
289
- // const width = (layout.width - (gap * (columnAmount - 1))) / columnAmount
290
- // const height = (layout.height - (gap * (rowAmount - 1))) / rowAmount
291
- // const x = columnIndex * width + (columnIndex * gap)
292
- // const y = rowIndex * height + (rowIndex * gap)
293
- // const translate: [number, number] = [x, y]
294
- // const scale: [number, number] = [width / layout.width, height / layout.height]
295
-
296
- // return {
297
- // translate,
298
- // scale
299
- // }
300
- // }
301
-
302
- export function calcGridContainerLayout (layout: Layout, container: DataFormatterContainer, amount: number): GridContainerPosition[] {
327
+ export function calcGridContainerLayout (layout: Layout, container: DataFormatterContainer, amount: number): ContainerPositionScaled[] {
303
328
  const { gap } = container
304
329
  const { rowAmount, columnAmount } = (container.rowAmount * container.columnAmount) >= amount
305
330
  // 如果container設定的rowAmount和columnAmount的乘積大於或等於amount,則使用目前設定
@@ -318,7 +343,7 @@ export function calcGridContainerLayout (layout: Layout, container: DataFormatte
318
343
  const translate: [number, number] = [x, y]
319
344
  const scale: [number, number] = [width / layout.width, height / layout.height]
320
345
 
321
- return {
346
+ return {
322
347
  slotIndex: index,
323
348
  rowIndex,
324
349
  columnIndex,
@@ -14,7 +14,7 @@ import type {
14
14
  ComputedDatumSeries,
15
15
  ComputedDataTypeMap,
16
16
  DataFormatterTypeMap,
17
- SeriesContainerPosition,
17
+ ContainerPosition,
18
18
  Layout } from '../../lib/core-types'
19
19
  import { calcSeriesContainerLayout } from './orbchartsUtils'
20
20
 
@@ -97,7 +97,7 @@ export const seriesContainerPositionObservable = ({ computedData$, fullDataForma
97
97
  computedData$: Observable<ComputedDataTypeMap<'series'>>
98
98
  fullDataFormatter$: Observable<DataFormatterTypeMap<'series'>>
99
99
  layout$: Observable<Layout>
100
- }): Observable<SeriesContainerPosition[]> => {
100
+ }): Observable<ContainerPosition[]> => {
101
101
 
102
102
  const gridContainerPosition$ = combineLatest({
103
103
  computedData: computedData$,
@@ -113,7 +113,7 @@ export const seriesContainerPositionObservable = ({ computedData$, fullDataForma
113
113
  // return data.computedData.map((seriesData, seriesIndex) => {
114
114
  // const columnIndex = seriesIndex % data.fullDataFormatter.container.columnAmount
115
115
  // const rowIndex = Math.floor(seriesIndex / data.fullDataFormatter.container.columnAmount)
116
- // const { startX, startY, centerX, centerY, width, height } = calcSeriesContainerPosition(data.layout, data.fullDataFormatter.container, rowIndex, columnIndex)
116
+ // const { startX, startY, centerX, centerY, width, height } = calcContainerPosition(data.layout, data.fullDataFormatter.container, rowIndex, columnIndex)
117
117
  // return {
118
118
  // slotIndex: seriesIndex,
119
119
  // rowIndex,
@@ -132,7 +132,7 @@ export const seriesContainerPositionObservable = ({ computedData$, fullDataForma
132
132
  // const columnIndex = 0
133
133
  // const rowIndex = 0
134
134
  // return data.computedData.map((seriesData, seriesIndex) => {
135
- // const { startX, startY, centerX, centerY, width, height } = calcSeriesContainerPosition(data.layout, data.fullDataFormatter.container, rowIndex, columnIndex)
135
+ // const { startX, startY, centerX, centerY, width, height } = calcContainerPosition(data.layout, data.fullDataFormatter.container, rowIndex, columnIndex)
136
136
  // return {
137
137
  // slotIndex: 0,
138
138
  // rowIndex,
@@ -153,7 +153,7 @@ export const seriesContainerPositionObservable = ({ computedData$, fullDataForma
153
153
  }
154
154
 
155
155
  export const seriesContainerPositionMapObservable = ({ seriesContainerPosition$, seriesLabels$, separateSeries$ }: {
156
- seriesContainerPosition$: Observable<SeriesContainerPosition[]>
156
+ seriesContainerPosition$: Observable<ContainerPosition[]>
157
157
  seriesLabels$: Observable<string[]>
158
158
  separateSeries$: Observable<boolean>
159
159
  }) => {
@@ -165,10 +165,10 @@ export const seriesContainerPositionMapObservable = ({ seriesContainerPosition$,
165
165
  switchMap(async (d) => d),
166
166
  map(data => {
167
167
  return data.separateSeries
168
- ? new Map<string, SeriesContainerPosition>(data.seriesLabels.map((seriesLabel, seriesIndex) => {
168
+ ? new Map<string, ContainerPosition>(data.seriesLabels.map((seriesLabel, seriesIndex) => {
169
169
  return [seriesLabel, data.seriesContainerPosition[seriesIndex] ?? data.seriesContainerPosition[0]]
170
170
  }))
171
- : new Map<string, SeriesContainerPosition>(data.seriesLabels.map((seriesLabel, seriesIndex) => {
171
+ : new Map<string, ContainerPosition>(data.seriesLabels.map((seriesLabel, seriesIndex) => {
172
172
  return [seriesLabel, data.seriesContainerPosition[0]]
173
173
  }))
174
174
  })
@@ -34,42 +34,53 @@ export const nodeListObservable = ({ computedData$ }: { computedData$: Observabl
34
34
  )
35
35
  }
36
36
 
37
- export const existCategoryLabelsObservable = ({ nodeList$, fullDataFormatter$ }: {
38
- nodeList$: Observable<ComputedDataTree[]>
39
- fullDataFormatter$: Observable<DataFormatterTree>
40
- }) => {
37
+ // export const categoryLabelsObservable = ({ nodeList$, fullDataFormatter$ }: {
38
+ // nodeList$: Observable<ComputedDataTree[]>
39
+ // fullDataFormatter$: Observable<DataFormatterTree>
40
+ // }) => {
41
41
 
42
- const categoryLabels$ = fullDataFormatter$.pipe(
43
- map(d => d.categoryLabels),
44
- distinctUntilChanged((a, b) => {
45
- return JSON.stringify(a).length === JSON.stringify(b).length
46
- }),
47
- )
42
+ // const categoryLabels$ = fullDataFormatter$.pipe(
43
+ // map(d => d.categoryLabels),
44
+ // distinctUntilChanged((a, b) => {
45
+ // return JSON.stringify(a).length === JSON.stringify(b).length
46
+ // }),
47
+ // )
48
48
 
49
- return combineLatest({
50
- nodeList: nodeList$,
51
- categoryLabels: categoryLabels$
52
- }).pipe(
53
- switchMap(async d => d),
54
- map(data => {
55
- const CurrentLabelSet = new Set(data.categoryLabels)
56
- const ExistLabelSet = new Set(
57
- data.nodeList.filter(node => node.visible).map(node => node.categoryLabel)
58
- )
59
- // 加入已存在的label(data.nodeList有,但是dataFormatter.categoryLabels沒有)
60
- Array.from(ExistLabelSet).forEach(label => {
61
- if (!CurrentLabelSet.has(label)) {
62
- CurrentLabelSet.add(label)
63
- }
64
- })
65
- // 移除不存在的label(dataFormatter.categoryLabels有,但是data.nodeList沒有)
66
- Array.from(CurrentLabelSet).forEach(label => {
67
- if (!ExistLabelSet.has(label)) {
68
- ExistLabelSet.delete(label)
69
- }
70
- })
49
+ // return combineLatest({
50
+ // nodeList: nodeList$,
51
+ // categoryLabels: categoryLabels$
52
+ // }).pipe(
53
+ // switchMap(async d => d),
54
+ // map(data => {
55
+ // const CurrentLabelSet = new Set(data.categoryLabels)
56
+ // const ExistLabelSet = new Set(
57
+ // data.nodeList.filter(node => node.visible).map(node => node.categoryLabel)
58
+ // )
59
+ // // 加入已存在的label(data.nodeList有,但是dataFormatter.categoryLabels沒有)
60
+ // Array.from(ExistLabelSet).forEach(label => {
61
+ // if (!CurrentLabelSet.has(label)) {
62
+ // CurrentLabelSet.add(label)
63
+ // }
64
+ // })
65
+ // // 移除不存在的label(dataFormatter.categoryLabels有,但是data.nodeList沒有)
66
+ // Array.from(CurrentLabelSet).forEach(label => {
67
+ // if (!ExistLabelSet.has(label)) {
68
+ // ExistLabelSet.delete(label)
69
+ // }
70
+ // })
71
71
 
72
- return Array.from(CurrentLabelSet)
72
+ // return Array.from(CurrentLabelSet)
73
+ // }),
74
+ // distinctUntilChanged((a, b) => {
75
+ // return JSON.stringify(a).length === JSON.stringify(b).length
76
+ // }),
77
+ // )
78
+ // }
79
+
80
+ export const categoryLabelsObservable = (CategoryDataMap$: Observable<Map<string, ComputedDataTree[]>>) => {
81
+ return CategoryDataMap$.pipe(
82
+ map(data => {
83
+ return Array.from(data.keys())
73
84
  }),
74
85
  distinctUntilChanged((a, b) => {
75
86
  return JSON.stringify(a).length === JSON.stringify(b).length
@@ -1,4 +1,5 @@
1
1
  import type {
2
+ ColorType,
2
3
  ValidatorResult,
3
4
  ToBeTypes,
4
5
  ToBeOption,
@@ -40,14 +41,14 @@ function getInvalidColumn<T> (data: T, rules: Partial<ValidatorRule<T>>) {
40
41
  }
41
42
  // "toBeOption" 的測試
42
43
  const testOption: {[key in ToBeOption]: (value: any) => boolean} = {
43
- ColorType: (value: any) => {
44
+ ColorType: (value: ColorType) => {
44
45
  return value === 'none'
45
46
  || value === 'series'
46
47
  || value === 'primary'
47
48
  || value === 'secondary'
48
49
  || value === 'white'
49
50
  || value === 'background'
50
- }
51
+ },
51
52
  }
52
53
 
53
54
  const failColumn = Object.keys(data).find((columnName: string) => {
@@ -1,19 +0,0 @@
1
- import * as d3 from 'd3';
2
- export declare const createAxisLinearScale: ({ maxValue, minValue, axisWidth, scaleDomain, scaleRange, }: {
3
- maxValue: number;
4
- minValue: number;
5
- axisWidth: number;
6
- scaleDomain: [number | "min" | "auto", number | "max" | "auto"];
7
- scaleRange: [number, number];
8
- }) => d3.ScaleLinear<number, number, never>;
9
- export declare const createAxisPointScale: ({ axisLabels, axisWidth, padding }: {
10
- axisLabels: string[];
11
- axisWidth: number;
12
- padding?: number;
13
- }) => d3.ScalePoint<string>;
14
- export declare const createAxisQuantizeScale: ({ axisLabels, axisWidth, padding, reverse }: {
15
- axisLabels: string[] | Date[];
16
- axisWidth: number;
17
- padding?: number;
18
- reverse?: boolean;
19
- }) => d3.ScaleQuantize<number, never>;
@@ -1,108 +0,0 @@
1
- import * as d3 from 'd3'
2
- import { DATA_FORMATTER_VALUE_AXIS_DEFAULT } from '../defaults'
3
-
4
- // scaleLinear - 連續資料對應到比例尺座標上
5
- export const createAxisLinearScale = ({
6
- maxValue = 1,
7
- minValue = 0,
8
- axisWidth,
9
- scaleDomain = DATA_FORMATTER_VALUE_AXIS_DEFAULT.scaleDomain,
10
- scaleRange = DATA_FORMATTER_VALUE_AXIS_DEFAULT.scaleRange,
11
- }: {
12
- maxValue: number
13
- minValue: number
14
- axisWidth: number
15
- scaleDomain: [number | 'min' | 'auto', number | 'max' | 'auto']
16
- scaleRange: [number, number] // 0-1
17
- }) => {
18
- // -- 無值補上預設值 --
19
- const domainMin: number | 'min' | 'auto' = scaleDomain[0] ?? DATA_FORMATTER_VALUE_AXIS_DEFAULT.scaleDomain[0]
20
- const domainMax: number | 'max' | 'auto' = scaleDomain[1] ?? DATA_FORMATTER_VALUE_AXIS_DEFAULT.scaleDomain[1]
21
- const rangeMin: number = scaleRange[0] ?? DATA_FORMATTER_VALUE_AXIS_DEFAULT.scaleRange[0]
22
- const rangeMax: number = scaleRange[1] ?? DATA_FORMATTER_VALUE_AXIS_DEFAULT.scaleRange[1]
23
-
24
- // -- 'auto' | 'max' | 'min' 替換成實際值 --
25
- let domainMinValue: number = (() => {
26
- if (domainMin === 'auto') {
27
- return minValue < 0 ? minValue : 0
28
- } else if (domainMin === 'min') {
29
- return minValue
30
- } else {
31
- return domainMin
32
- }
33
- })()
34
-
35
- let domainMaxValue: number = (() => {
36
- if (domainMax === 'auto') {
37
- return maxValue >= 0 ? maxValue : 0
38
- } else if (domainMax === 'max') {
39
- return maxValue
40
- } else {
41
- return domainMax
42
- }
43
- })()
44
- // let rangeMinValue = axisWidth * rangeMin
45
- // let rangeMaxValue = axisWidth * rangeMax
46
-
47
- // -- 計算padding --
48
- // if (padding > 0) {
49
- // const stepAmount = maxValue - minValue + (padding * 2)
50
- // const eachStepWidth = axisWidth / stepAmount
51
- // const paddingWidth = eachStepWidth * padding
52
- // rangeMinValue += paddingWidth
53
- // rangeMaxValue -= paddingWidth
54
- // }
55
-
56
- // -- 依場景大小換算 --
57
- const axisDomainMinValue = maxValue - (maxValue - domainMinValue) / (1 - rangeMin)
58
- const axisDomainMaxValue = domainMaxValue / rangeMax
59
-
60
- // return d3.scaleLinear()
61
- // .domain([domainMinValue, domainMaxValue])
62
- // .range([rangeMinValue, rangeMaxValue])
63
- return d3.scaleLinear()
64
- .domain([axisDomainMinValue, axisDomainMaxValue])
65
- .range([0, axisWidth])
66
- }
67
-
68
- // scalePoint - 非連續資料對應到比例尺座標上
69
- export const createAxisPointScale = ({ axisLabels, axisWidth, padding = 0.5 }: {
70
- axisLabels: string[]
71
- axisWidth: number
72
- padding?: number
73
- // reverse?: boolean
74
- }) => {
75
- let range: [d3.NumberValue, d3.NumberValue] = [0, axisWidth]
76
-
77
- return d3.scalePoint()
78
- .domain(axisLabels)
79
- .range(range)
80
- .padding(padding)
81
- }
82
-
83
- // scaleQuantize - 比例尺座標對應非連續資料索引
84
- export const createAxisQuantizeScale = ({ axisLabels, axisWidth, padding = 0, reverse = false }:{
85
- axisLabels: string[] | Date[],
86
- axisWidth: number
87
- padding?: number
88
- reverse?: boolean
89
- }) => {
90
-
91
- let range: number[] = axisLabels.map((d: string | Date, i: number) => i)
92
- if (reverse) {
93
- range.reverse()
94
- }
95
- // if (reverse) {
96
- // range = axisLabels.map((d: string | Date, i: number) => axisLabels.length - 1 - i)
97
- // } else {
98
- // range = axisLabels.map((d: string | Date, i: number) => i)
99
- // }
100
- const step = range.length - 1 + (padding * 2) // 圖軸刻度分段數量
101
- const stepWidth = axisWidth / step
102
- const rangePadding = stepWidth * padding - (stepWidth * 0.5) // 實際要計算的範圍是圖軸左右那邊增加0.5
103
-
104
- // console.log('rangePadding', rangePadding)
105
- return d3.scaleQuantize<number>()
106
- .domain([rangePadding, axisWidth - rangePadding])
107
- .range(range)
108
- }