@orbcharts/core 3.0.6 → 4.0.0-pre-alpha.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +200 -200
- package/dist/orbcharts-core.es.js +2795 -6591
- package/dist/orbcharts-core.umd.js +6 -6
- package/dist/src/OrbCharts.d.ts +18 -0
- package/dist/src/chart/createChart.d.ts +3 -0
- package/dist/src/chart/createGraphData.d.ts +3 -0
- package/dist/src/chart/createGridData.d.ts +3 -0
- package/dist/src/chart/createMultivariateData.d.ts +3 -0
- package/dist/src/chart/createSeriesData.d.ts +3 -0
- package/dist/src/chart/createTreeData.d.ts +3 -0
- package/dist/src/chart/defaults.d.ts +5 -0
- package/dist/src/defineCanvasLayer.d.ts +16 -0
- package/dist/src/defineCanvasPlugin.d.ts +22 -0
- package/dist/src/defineSVGLayer.d.ts +16 -0
- package/dist/src/defineSVGPlugin.d.ts +22 -0
- package/dist/src/index.d.ts +6 -14
- package/dist/src/layer/createLayer.d.ts +3 -0
- package/dist/src/plugin/createPlugin.d.ts +3 -0
- package/dist/src/test/createGraphData.test.d.ts +1 -0
- package/dist/src/test/createTreeData.test.d.ts +1 -0
- package/dist/src/test/simple-graph-test.d.ts +74 -0
- package/dist/src/test/simple-tree-test.d.ts +13 -0
- package/dist/src/types/Chart.d.ts +39 -0
- package/dist/src/types/ChartContext.d.ts +27 -0
- package/dist/src/types/Common.d.ts +3 -0
- package/dist/src/types/Encoding.d.ts +33 -0
- package/dist/src/types/Event.d.ts +12 -0
- package/dist/src/types/Layers.d.ts +55 -0
- package/dist/src/types/ModelData.d.ts +70 -0
- package/dist/src/types/Plugin.d.ts +39 -0
- package/dist/src/types/RawData.d.ts +18 -0
- package/dist/src/types/RenderData.d.ts +4 -0
- package/dist/src/types/Theme.d.ts +17 -0
- package/dist/src/types/Validator.d.ts +20 -0
- package/dist/src/types/index.d.ts +12 -0
- package/dist/src/utils/aggregateUtils.d.ts +37 -0
- package/dist/src/utils/colorUtils.d.ts +22 -0
- package/dist/src/utils/commonUtils.d.ts +3 -5
- package/dist/src/utils/dom-lifecycle.d.ts +37 -0
- package/dist/src/utils/dom.d.ts +6 -0
- package/dist/src/utils/index.d.ts +5 -1
- package/dist/src/utils/observables.d.ts +1 -25
- package/dist/src/utils/orbchartsUtils.d.ts +2 -53
- package/dist/src/utils/validator.d.ts +2 -2
- package/dist/test/aggregateTest.d.ts +1 -0
- package/package.json +59 -46
- package/src/OrbCharts.ts +35 -0
- package/src/chart/createChart.ts +997 -0
- package/src/chart/createGraphData.ts +391 -0
- package/src/chart/createGridData.ts +247 -0
- package/src/chart/createMultivariateData.ts +181 -0
- package/src/chart/createSeriesData.ts +297 -0
- package/src/chart/createTreeData.ts +344 -0
- package/src/chart/defaults.ts +100 -0
- package/src/defineCanvasLayer.ts +24 -0
- package/src/defineCanvasPlugin.ts +39 -0
- package/src/defineSVGLayer.ts +24 -0
- package/src/defineSVGPlugin.ts +39 -0
- package/src/index.ts +8 -20
- package/src/layer/createLayer.ts +138 -0
- package/src/plugin/createPlugin.ts +470 -0
- package/src/test/createGraphData.test.ts +103 -0
- package/src/test/createTreeData.test.ts +97 -0
- package/src/test/simple-graph-test.js +51 -0
- package/src/test/simple-tree-test.js +58 -0
- package/src/types/Chart.ts +62 -0
- package/src/types/ChartContext.ts +42 -0
- package/src/types/Common.ts +5 -0
- package/src/types/Encoding.ts +43 -0
- package/src/types/Event.ts +26 -0
- package/src/types/Layers.ts +93 -0
- package/src/types/ModelData.ts +95 -0
- package/src/types/Plugin.ts +98 -0
- package/src/types/RawData.ts +67 -0
- package/src/types/RenderData.ts +16 -0
- package/src/types/Theme.ts +21 -0
- package/src/types/Validator.ts +36 -0
- package/src/types/index.ts +12 -0
- package/src/utils/aggregateUtils.ts +99 -0
- package/src/utils/colorUtils.ts +63 -0
- package/src/utils/commonUtils.ts +56 -55
- package/src/utils/dom-lifecycle.ts +164 -0
- package/src/utils/dom.ts +55 -0
- package/src/utils/errorMessage.ts +40 -40
- package/src/utils/index.ts +8 -4
- package/src/utils/observables.ts +16 -308
- package/src/utils/orbchartsUtils.ts +9 -396
- package/src/utils/validator.ts +127 -126
- package/dist/lib/core-types.d.ts +0 -1
- package/dist/src/AbstractChart.d.ts +0 -19
- package/dist/src/GridChart.d.ts +0 -6
- package/dist/src/MultiGridChart.d.ts +0 -6
- package/dist/src/MultiValueChart.d.ts +0 -6
- package/dist/src/RelationshipChart.d.ts +0 -6
- package/dist/src/SeriesChart.d.ts +0 -6
- package/dist/src/TreeChart.d.ts +0 -6
- package/dist/src/base/createBaseChart.d.ts +0 -3
- package/dist/src/base/createBasePlugin.d.ts +0 -3
- package/dist/src/base/validators/chartOptionsValidator.d.ts +0 -3
- package/dist/src/base/validators/chartParamsValidator.d.ts +0 -3
- package/dist/src/base/validators/elementValidator.d.ts +0 -3
- package/dist/src/base/validators/pluginsValidator.d.ts +0 -3
- package/dist/src/defaults.d.ts +0 -25
- package/dist/src/defineGridPlugin.d.ts +0 -1
- package/dist/src/defineMultiGridPlugin.d.ts +0 -1
- package/dist/src/defineMultiValuePlugin.d.ts +0 -1
- package/dist/src/defineNoneDataPlugin.d.ts +0 -1
- package/dist/src/defineRelationshipPlugin.d.ts +0 -1
- package/dist/src/defineSeriesPlugin.d.ts +0 -1
- package/dist/src/defineTreePlugin.d.ts +0 -1
- package/dist/src/grid/computedDataFn.d.ts +0 -4
- package/dist/src/grid/contextObserverCallback.d.ts +0 -3
- package/dist/src/grid/dataFormatterValidator.d.ts +0 -3
- package/dist/src/grid/dataValidator.d.ts +0 -3
- package/dist/src/grid/gridObservables.d.ts +0 -64
- package/dist/src/multiGrid/computedDataFn.d.ts +0 -3
- package/dist/src/multiGrid/contextObserverCallback.d.ts +0 -3
- package/dist/src/multiGrid/dataFormatterValidator.d.ts +0 -3
- package/dist/src/multiGrid/dataValidator.d.ts +0 -3
- package/dist/src/multiGrid/multiGridObservables.d.ts +0 -16
- package/dist/src/multiValue/computedDataFn.d.ts +0 -3
- package/dist/src/multiValue/contextObserverCallback.d.ts +0 -3
- package/dist/src/multiValue/dataFormatterValidator.d.ts +0 -3
- package/dist/src/multiValue/dataValidator.d.ts +0 -3
- package/dist/src/multiValue/multiValueObservables.d.ts +0 -130
- package/dist/src/relationship/computedDataFn.d.ts +0 -3
- package/dist/src/relationship/contextObserverCallback.d.ts +0 -3
- package/dist/src/relationship/dataFormatterValidator.d.ts +0 -3
- package/dist/src/relationship/dataValidator.d.ts +0 -3
- package/dist/src/relationship/relationshipObservables.d.ts +0 -13
- package/dist/src/series/computedDataFn.d.ts +0 -3
- package/dist/src/series/contextObserverCallback.d.ts +0 -3
- package/dist/src/series/dataFormatterValidator.d.ts +0 -3
- package/dist/src/series/dataValidator.d.ts +0 -3
- package/dist/src/series/seriesObservables.d.ts +0 -37
- package/dist/src/tree/computedDataFn.d.ts +0 -3
- package/dist/src/tree/contextObserverCallback.d.ts +0 -3
- package/dist/src/tree/dataFormatterValidator.d.ts +0 -3
- package/dist/src/tree/dataValidator.d.ts +0 -3
- package/dist/src/tree/treeObservables.d.ts +0 -10
- package/dist/src/utils/d3Scale.d.ts +0 -28
- package/lib/core-types.ts +0 -7
- package/src/AbstractChart.ts +0 -57
- package/src/GridChart.ts +0 -25
- package/src/MultiGridChart.ts +0 -25
- package/src/MultiValueChart.ts +0 -25
- package/src/RelationshipChart.ts +0 -25
- package/src/SeriesChart.ts +0 -25
- package/src/TreeChart.ts +0 -25
- package/src/base/createBaseChart.ts +0 -524
- package/src/base/createBasePlugin.ts +0 -154
- package/src/base/validators/chartOptionsValidator.ts +0 -24
- package/src/base/validators/chartParamsValidator.ts +0 -134
- package/src/base/validators/elementValidator.ts +0 -14
- package/src/base/validators/pluginsValidator.ts +0 -15
- package/src/defaults.ts +0 -284
- package/src/defineGridPlugin.ts +0 -3
- package/src/defineMultiGridPlugin.ts +0 -3
- package/src/defineMultiValuePlugin.ts +0 -3
- package/src/defineNoneDataPlugin.ts +0 -4
- package/src/defineRelationshipPlugin.ts +0 -3
- package/src/defineSeriesPlugin.ts +0 -3
- package/src/defineTreePlugin.ts +0 -3
- package/src/grid/computedDataFn.ts +0 -129
- package/src/grid/contextObserverCallback.ts +0 -209
- package/src/grid/dataFormatterValidator.ts +0 -126
- package/src/grid/dataValidator.ts +0 -13
- package/src/grid/gridObservables.ts +0 -699
- package/src/multiGrid/computedDataFn.ts +0 -123
- package/src/multiGrid/contextObserverCallback.ts +0 -109
- package/src/multiGrid/dataFormatterValidator.ts +0 -121
- package/src/multiGrid/dataValidator.ts +0 -13
- package/src/multiGrid/multiGridObservables.ts +0 -367
- package/src/multiValue/computedDataFn.ts +0 -113
- package/src/multiValue/contextObserverCallback.ts +0 -328
- package/src/multiValue/dataFormatterValidator.ts +0 -95
- package/src/multiValue/dataValidator.ts +0 -13
- package/src/multiValue/multiValueObservables.ts +0 -865
- package/src/relationship/computedDataFn.ts +0 -159
- package/src/relationship/contextObserverCallback.ts +0 -80
- package/src/relationship/dataFormatterValidator.ts +0 -14
- package/src/relationship/dataValidator.ts +0 -14
- package/src/relationship/relationshipObservables.ts +0 -85
- package/src/series/computedDataFn.ts +0 -88
- package/src/series/contextObserverCallback.ts +0 -132
- package/src/series/dataFormatterValidator.ts +0 -47
- package/src/series/dataValidator.ts +0 -13
- package/src/series/seriesObservables.ts +0 -210
- package/src/tree/computedDataFn.ts +0 -129
- package/src/tree/contextObserverCallback.ts +0 -58
- package/src/tree/dataFormatterValidator.ts +0 -14
- package/src/tree/dataValidator.ts +0 -14
- package/src/tree/treeObservables.ts +0 -106
- package/src/utils/d3Scale.ts +0 -198
- package/tsconfig.base.json +0 -14
- package/tsconfig.json +0 -3
- package/vite-env.d.ts +0 -7
- package/vite.config.js +0 -23
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
import type { RawData, RawDataColumn, Encoding, ModelDataTree, ModelDatumTree, Theme } from '../types'
|
|
2
|
+
import { aggregate } from '../utils/aggregateUtils'
|
|
3
|
+
import { getColorByFrom } from '../utils/colorUtils'
|
|
4
|
+
|
|
5
|
+
export const createTreeData = (rawData: RawData, encoding: Encoding, theme: Theme): ModelDataTree[] => {
|
|
6
|
+
// 依據 dataset 欄位將資料分組
|
|
7
|
+
const datasetMap = new Map<string, RawDataColumn[]>()
|
|
8
|
+
|
|
9
|
+
// 判斷是一維陣列還是二維陣列
|
|
10
|
+
const is2DArray = Array.isArray(rawData[0])
|
|
11
|
+
|
|
12
|
+
if (is2DArray) {
|
|
13
|
+
// 二維陣列:每個子陣列代表一個 dataset
|
|
14
|
+
(rawData as RawDataColumn[][]).forEach((datasetArray, datasetIndex) => {
|
|
15
|
+
datasetArray.forEach((d) => {
|
|
16
|
+
const datasetKey = (d as any)[encoding.dataset.from] || `dataset-${datasetIndex}`
|
|
17
|
+
if (!datasetMap.has(datasetKey)) {
|
|
18
|
+
datasetMap.set(datasetKey, [])
|
|
19
|
+
}
|
|
20
|
+
datasetMap.get(datasetKey)!.push(d)
|
|
21
|
+
})
|
|
22
|
+
})
|
|
23
|
+
} else {
|
|
24
|
+
// 一維陣列:依據 dataset 欄位分組
|
|
25
|
+
(rawData as RawDataColumn[]).forEach((d) => {
|
|
26
|
+
const datasetKey = (d as any)[encoding.dataset.from] || 'default'
|
|
27
|
+
if (!datasetMap.has(datasetKey)) {
|
|
28
|
+
datasetMap.set(datasetKey, [])
|
|
29
|
+
}
|
|
30
|
+
datasetMap.get(datasetKey)!.push(d)
|
|
31
|
+
})
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// 建立排序後的 dataset 名稱陣列
|
|
35
|
+
let sortedDatasetNames: string[] = Array.from(datasetMap.keys())
|
|
36
|
+
if (Array.isArray(encoding.dataset.sort)) {
|
|
37
|
+
sortedDatasetNames = encoding.dataset.sort.filter(name => datasetMap.has(name))
|
|
38
|
+
.concat(sortedDatasetNames.filter(name => !encoding.dataset.sort.includes(name)))
|
|
39
|
+
} else if (encoding.dataset.sort === 'original') {
|
|
40
|
+
const datasetOrder: string[] = []
|
|
41
|
+
if (is2DArray) {
|
|
42
|
+
(rawData as RawDataColumn[][]).forEach((datasetArray, datasetIndex) => {
|
|
43
|
+
datasetArray.forEach((d) => {
|
|
44
|
+
const datasetKey = (d as any)[encoding.dataset.from] || `dataset-${datasetIndex}`
|
|
45
|
+
if (!datasetOrder.includes(datasetKey)) {
|
|
46
|
+
datasetOrder.push(datasetKey)
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
})
|
|
50
|
+
} else {
|
|
51
|
+
(rawData as RawDataColumn[]).forEach((d) => {
|
|
52
|
+
const datasetKey = (d as any)[encoding.dataset.from] || 'default'
|
|
53
|
+
if (!datasetOrder.includes(datasetKey)) {
|
|
54
|
+
datasetOrder.push(datasetKey)
|
|
55
|
+
}
|
|
56
|
+
})
|
|
57
|
+
}
|
|
58
|
+
sortedDatasetNames = datasetOrder
|
|
59
|
+
} else if (encoding.dataset.sort === 'alphabetical') {
|
|
60
|
+
sortedDatasetNames.sort((a, b) => a.localeCompare(b, undefined, { numeric: true }))
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// 對每個 dataset 進行 tree 資料的處理
|
|
64
|
+
const result: ModelDataTree[] = []
|
|
65
|
+
sortedDatasetNames.forEach((datasetName, datasetIndex) => {
|
|
66
|
+
const data = datasetMap.get(datasetName)!
|
|
67
|
+
|
|
68
|
+
// 建立 dataset 內排序後的 series 名稱陣列(僅作為屬性與色彩來源,不做拆樹)
|
|
69
|
+
const seriesMap = new Map<string, RawDataColumn[]>()
|
|
70
|
+
data.forEach((d) => {
|
|
71
|
+
const seriesKey = (d as any)[encoding.series.from] || 'default'
|
|
72
|
+
if (!seriesMap.has(seriesKey)) {
|
|
73
|
+
seriesMap.set(seriesKey, [])
|
|
74
|
+
}
|
|
75
|
+
seriesMap.get(seriesKey)!.push(d)
|
|
76
|
+
})
|
|
77
|
+
let sortedSeriesNames: string[] = Array.from(seriesMap.keys())
|
|
78
|
+
if (Array.isArray(encoding.series.sort)) {
|
|
79
|
+
sortedSeriesNames = encoding.series.sort.filter(name => seriesMap.has(name))
|
|
80
|
+
.concat(sortedSeriesNames.filter(name => !encoding.series.sort.includes(name)))
|
|
81
|
+
} else if (encoding.series.sort === 'original') {
|
|
82
|
+
const seriesOrder: string[] = []
|
|
83
|
+
data.forEach((d) => {
|
|
84
|
+
const seriesKey = (d as any)[encoding.series.from] || 'default'
|
|
85
|
+
if (!seriesOrder.includes(seriesKey)) {
|
|
86
|
+
seriesOrder.push(seriesKey)
|
|
87
|
+
}
|
|
88
|
+
})
|
|
89
|
+
sortedSeriesNames = seriesOrder
|
|
90
|
+
} else if (encoding.series.sort === 'alphabetical') {
|
|
91
|
+
sortedSeriesNames.sort((a, b) => a.localeCompare(b, undefined, { numeric: true }))
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// 建立 dataset 內排序後的 category 名稱陣列(僅作為屬性與色彩來源,不做拆樹)
|
|
95
|
+
const categoryMap = new Map<string, RawDataColumn[]>()
|
|
96
|
+
data.forEach((d) => {
|
|
97
|
+
const categoryKey = (d as any)[encoding.category.from] || 'default'
|
|
98
|
+
if (!categoryMap.has(categoryKey)) {
|
|
99
|
+
categoryMap.set(categoryKey, [])
|
|
100
|
+
}
|
|
101
|
+
categoryMap.get(categoryKey)!.push(d)
|
|
102
|
+
})
|
|
103
|
+
let sortedCategoryNames: string[] = Array.from(categoryMap.keys())
|
|
104
|
+
if (Array.isArray(encoding.category.sort)) {
|
|
105
|
+
sortedCategoryNames = encoding.category.sort.filter(name => categoryMap.has(name))
|
|
106
|
+
.concat(sortedCategoryNames.filter(name => !encoding.category.sort.includes(name)))
|
|
107
|
+
} else if (encoding.category.sort === 'original') {
|
|
108
|
+
const categoryOrder: string[] = []
|
|
109
|
+
data.forEach((d) => {
|
|
110
|
+
const categoryKey = (d as any)[encoding.category.from] || 'default'
|
|
111
|
+
if (!categoryOrder.includes(categoryKey)) {
|
|
112
|
+
categoryOrder.push(categoryKey)
|
|
113
|
+
}
|
|
114
|
+
})
|
|
115
|
+
sortedCategoryNames = categoryOrder
|
|
116
|
+
} else if (encoding.category.sort === 'alphabetical') {
|
|
117
|
+
sortedCategoryNames.sort((a, b) => a.localeCompare(b, undefined, { numeric: true }))
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// 收集所有節點並依據 id 進行聚合(同一 dataset 內)
|
|
121
|
+
const nodeMap = new Map<string, RawDataColumn[]>()
|
|
122
|
+
data.forEach((d) => {
|
|
123
|
+
const nodeId = d.id || 'unknown'
|
|
124
|
+
if (!nodeMap.has(nodeId)) {
|
|
125
|
+
nodeMap.set(nodeId, [])
|
|
126
|
+
}
|
|
127
|
+
nodeMap.get(nodeId)!.push(d)
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
let globalNodeIndex = 0
|
|
131
|
+
const defaultSeriesName = sortedSeriesNames[0] || 'default'
|
|
132
|
+
const defaultSeriesIndex = sortedSeriesNames.indexOf(defaultSeriesName)
|
|
133
|
+
|
|
134
|
+
// 為每個 id 建立 ModelDatumTree,並進行聚合
|
|
135
|
+
const treeNodeMap = new Map<string, ModelDatumTree>()
|
|
136
|
+
|
|
137
|
+
Array.from(nodeMap.entries()).forEach(([nodeId, nodeItems]) => {
|
|
138
|
+
const firstItem = nodeItems[0]
|
|
139
|
+
const seriesName = (firstItem as any)[encoding.series.from] || 'default'
|
|
140
|
+
const seriesIndex = sortedSeriesNames.indexOf(seriesName)
|
|
141
|
+
const categoryName = (firstItem as any)[encoding.category.from] || 'default'
|
|
142
|
+
const categoryIndex = sortedCategoryNames.indexOf(categoryName)
|
|
143
|
+
|
|
144
|
+
if (encoding.value.aggregate === 'none') {
|
|
145
|
+
// 不聚合,使用第一筆資料
|
|
146
|
+
const value = (firstItem as any)[encoding.value.from]
|
|
147
|
+
const treeNode: ModelDatumTree = {
|
|
148
|
+
id: nodeId,
|
|
149
|
+
index: globalNodeIndex++,
|
|
150
|
+
modelType: 'tree',
|
|
151
|
+
name: firstItem.name || nodeId,
|
|
152
|
+
data: firstItem.data,
|
|
153
|
+
value: typeof value === 'number' ? value : null,
|
|
154
|
+
color: getColorByFrom(encoding.color.from, {
|
|
155
|
+
index: globalNodeIndex - 1,
|
|
156
|
+
seriesIndex,
|
|
157
|
+
categoryIndex,
|
|
158
|
+
datasetIndex
|
|
159
|
+
}, theme),
|
|
160
|
+
parent: firstItem.parent || null,
|
|
161
|
+
parentIndex: null, // 稍後設定
|
|
162
|
+
depth: 0, // 稍後計算
|
|
163
|
+
seq: 0, // 稍後計算
|
|
164
|
+
children: [],
|
|
165
|
+
series: seriesName,
|
|
166
|
+
seriesIndex,
|
|
167
|
+
category: categoryName,
|
|
168
|
+
categoryIndex,
|
|
169
|
+
}
|
|
170
|
+
treeNodeMap.set(nodeId, treeNode)
|
|
171
|
+
} else {
|
|
172
|
+
// 進行聚合,將相同 dataset, id 的資料合併
|
|
173
|
+
const values: (number | null)[] = nodeItems.map(d => {
|
|
174
|
+
if (encoding.value.aggregate === 'count') {
|
|
175
|
+
return 1 // count 聚合時每筆資料計為 1
|
|
176
|
+
}
|
|
177
|
+
const value = (d as any)[encoding.value.from]
|
|
178
|
+
return typeof value === 'number' ? value : null
|
|
179
|
+
})
|
|
180
|
+
|
|
181
|
+
const aggregatedValue = aggregate(values, encoding.value.aggregate)
|
|
182
|
+
|
|
183
|
+
const treeNode: ModelDatumTree = {
|
|
184
|
+
id: nodeId,
|
|
185
|
+
index: globalNodeIndex++,
|
|
186
|
+
modelType: 'tree',
|
|
187
|
+
name: firstItem.name || nodeId,
|
|
188
|
+
data: firstItem.data,
|
|
189
|
+
value: aggregatedValue,
|
|
190
|
+
color: getColorByFrom(encoding.color.from, {
|
|
191
|
+
index: globalNodeIndex - 1,
|
|
192
|
+
seriesIndex,
|
|
193
|
+
categoryIndex,
|
|
194
|
+
datasetIndex
|
|
195
|
+
}, theme),
|
|
196
|
+
parent: firstItem.parent || null,
|
|
197
|
+
parentIndex: null, // 稍後設定
|
|
198
|
+
depth: 0, // 稍後計算
|
|
199
|
+
seq: 0, // 稍後計算
|
|
200
|
+
children: [],
|
|
201
|
+
series: seriesName,
|
|
202
|
+
seriesIndex,
|
|
203
|
+
category: categoryName,
|
|
204
|
+
categoryIndex,
|
|
205
|
+
}
|
|
206
|
+
treeNodeMap.set(nodeId, treeNode)
|
|
207
|
+
}
|
|
208
|
+
})
|
|
209
|
+
|
|
210
|
+
// 建立樹狀結構
|
|
211
|
+
const allNodes = Array.from(treeNodeMap.values())
|
|
212
|
+
const nodeIndexMap = new Map<string, number>()
|
|
213
|
+
allNodes.forEach(node => {
|
|
214
|
+
nodeIndexMap.set(node.id, node.index)
|
|
215
|
+
})
|
|
216
|
+
|
|
217
|
+
// 設定 parentIndex
|
|
218
|
+
allNodes.forEach(node => {
|
|
219
|
+
if (node.parent && nodeIndexMap.has(node.parent)) {
|
|
220
|
+
node.parentIndex = nodeIndexMap.get(node.parent)!
|
|
221
|
+
}
|
|
222
|
+
})
|
|
223
|
+
|
|
224
|
+
// 建立父子關係
|
|
225
|
+
const rootNodes: ModelDatumTree[] = []
|
|
226
|
+
const childrenMap = new Map<string, ModelDatumTree[]>()
|
|
227
|
+
|
|
228
|
+
allNodes.forEach(node => {
|
|
229
|
+
if (node.parent === null) {
|
|
230
|
+
rootNodes.push(node)
|
|
231
|
+
} else {
|
|
232
|
+
if (!childrenMap.has(node.parent)) {
|
|
233
|
+
childrenMap.set(node.parent, [])
|
|
234
|
+
}
|
|
235
|
+
childrenMap.get(node.parent)!.push(node)
|
|
236
|
+
}
|
|
237
|
+
})
|
|
238
|
+
|
|
239
|
+
// 遞迴建立完整樹狀結構並計算 depth 和 seq
|
|
240
|
+
function buildTree(node: ModelDatumTree, depth: number): ModelDatumTree {
|
|
241
|
+
node.depth = depth
|
|
242
|
+
const children = childrenMap.get(node.id) || []
|
|
243
|
+
|
|
244
|
+
// 根據 value.sort 對子節點進行排序
|
|
245
|
+
if (encoding.value.sort === 'asc') {
|
|
246
|
+
children.sort((a, b) => {
|
|
247
|
+
if (a.value === null && b.value === null) return 0
|
|
248
|
+
if (a.value === null) return 1
|
|
249
|
+
if (b.value === null) return -1
|
|
250
|
+
return a.value - b.value
|
|
251
|
+
})
|
|
252
|
+
} else if (encoding.value.sort === 'desc') {
|
|
253
|
+
children.sort((a, b) => {
|
|
254
|
+
if (a.value === null && b.value === null) return 0
|
|
255
|
+
if (a.value === null) return 1
|
|
256
|
+
if (b.value === null) return -1
|
|
257
|
+
return b.value - a.value
|
|
258
|
+
})
|
|
259
|
+
}
|
|
260
|
+
// 'original' 不需要額外排序,保持原始順序
|
|
261
|
+
|
|
262
|
+
// 設定 seq
|
|
263
|
+
children.forEach((child, index) => {
|
|
264
|
+
child.seq = index
|
|
265
|
+
})
|
|
266
|
+
|
|
267
|
+
// 遞迴處理子節點
|
|
268
|
+
node.children = children.map(child => buildTree(child, depth + 1))
|
|
269
|
+
|
|
270
|
+
return node
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// 建立完整的樹
|
|
274
|
+
const trees = rootNodes.map(root => buildTree(root, 0))
|
|
275
|
+
|
|
276
|
+
// 如果有多個根節點,建立一個虛擬根節點
|
|
277
|
+
if (trees.length === 0) {
|
|
278
|
+
// 沒有資料,建立空樹
|
|
279
|
+
result.push({
|
|
280
|
+
id: `empty-${datasetName}`,
|
|
281
|
+
index: globalNodeIndex++,
|
|
282
|
+
modelType: 'tree',
|
|
283
|
+
name: 'Empty Tree',
|
|
284
|
+
data: {},
|
|
285
|
+
value: null,
|
|
286
|
+
color: getColorByFrom(encoding.color.from, {
|
|
287
|
+
index: globalNodeIndex - 1,
|
|
288
|
+
seriesIndex: defaultSeriesIndex < 0 ? 0 : defaultSeriesIndex,
|
|
289
|
+
categoryIndex: 0,
|
|
290
|
+
datasetIndex
|
|
291
|
+
}, theme),
|
|
292
|
+
parent: null,
|
|
293
|
+
parentIndex: null,
|
|
294
|
+
depth: 0,
|
|
295
|
+
seq: 0,
|
|
296
|
+
children: [],
|
|
297
|
+
series: defaultSeriesName,
|
|
298
|
+
seriesIndex: defaultSeriesIndex < 0 ? 0 : defaultSeriesIndex,
|
|
299
|
+
category: 'default',
|
|
300
|
+
categoryIndex: 0,
|
|
301
|
+
})
|
|
302
|
+
} else if (trees.length === 1) {
|
|
303
|
+
// 單一根節點
|
|
304
|
+
result.push(trees[0])
|
|
305
|
+
} else {
|
|
306
|
+
const virtualRoot: ModelDatumTree = {
|
|
307
|
+
id: `virtual-root-${datasetName}`,
|
|
308
|
+
index: globalNodeIndex++,
|
|
309
|
+
modelType: 'tree',
|
|
310
|
+
name: `Virtual Root (${datasetName})`,
|
|
311
|
+
data: {},
|
|
312
|
+
value: null,
|
|
313
|
+
color: getColorByFrom(encoding.color.from, {
|
|
314
|
+
index: globalNodeIndex - 1,
|
|
315
|
+
seriesIndex: defaultSeriesIndex < 0 ? 0 : defaultSeriesIndex,
|
|
316
|
+
categoryIndex: 0,
|
|
317
|
+
datasetIndex
|
|
318
|
+
}, theme),
|
|
319
|
+
parent: null,
|
|
320
|
+
parentIndex: null,
|
|
321
|
+
depth: 0,
|
|
322
|
+
seq: 0,
|
|
323
|
+
children: trees.map((tree, index) => {
|
|
324
|
+
const updateDepth = (node: ModelDatumTree, newDepth: number): ModelDatumTree => {
|
|
325
|
+
node.depth = newDepth
|
|
326
|
+
if (newDepth === 1) {
|
|
327
|
+
node.seq = index
|
|
328
|
+
}
|
|
329
|
+
node.children = node.children.map(child => updateDepth(child, newDepth + 1))
|
|
330
|
+
return node
|
|
331
|
+
}
|
|
332
|
+
return updateDepth(tree, 1)
|
|
333
|
+
}),
|
|
334
|
+
series: defaultSeriesName,
|
|
335
|
+
seriesIndex: defaultSeriesIndex < 0 ? 0 : defaultSeriesIndex,
|
|
336
|
+
category: 'default',
|
|
337
|
+
categoryIndex: 0,
|
|
338
|
+
}
|
|
339
|
+
result.push(virtualRoot)
|
|
340
|
+
}
|
|
341
|
+
})
|
|
342
|
+
|
|
343
|
+
return result
|
|
344
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import type { Encoding, Theme, SizeConfig } from '../types'
|
|
2
|
+
|
|
3
|
+
// export const DEFAULT_CHART_WIDTH = 800
|
|
4
|
+
|
|
5
|
+
// export const DEFAULT_CHART_HEIGHT = 500
|
|
6
|
+
|
|
7
|
+
export const DEFAULT_SIZE_CONFIG: SizeConfig = {
|
|
8
|
+
width: 'auto',
|
|
9
|
+
height: 'auto',
|
|
10
|
+
resizeDebounce: 50
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const DEFAULT_DATA_ENCODING: Encoding = {
|
|
14
|
+
dataset: {
|
|
15
|
+
from: 'dataset',
|
|
16
|
+
sort: 'original'
|
|
17
|
+
},
|
|
18
|
+
series: {
|
|
19
|
+
from: 'series',
|
|
20
|
+
sort: 'original'
|
|
21
|
+
},
|
|
22
|
+
category: {
|
|
23
|
+
from: 'category',
|
|
24
|
+
sort: 'original'
|
|
25
|
+
},
|
|
26
|
+
value: {
|
|
27
|
+
from: 'value',
|
|
28
|
+
sort: 'original',
|
|
29
|
+
aggregate: 'none'
|
|
30
|
+
},
|
|
31
|
+
multivariate: [
|
|
32
|
+
{ from: 'x', name: 'x' },
|
|
33
|
+
{ from: 'y', name: 'y' },
|
|
34
|
+
{ from: 'z', name: 'z' },
|
|
35
|
+
],
|
|
36
|
+
color: {
|
|
37
|
+
from: 'series',
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export const DEFAULT_THEME: Theme = {
|
|
42
|
+
// colorScheme: 'light',
|
|
43
|
+
// colors: {
|
|
44
|
+
// light: {
|
|
45
|
+
// data: ['#5B8FF9', '#61DDAA', '#65789B', '#F6BD16', '#7262FD', '#78D3F8', '#9661BC', '#F6903D', '#008685', '#F08BB4'],
|
|
46
|
+
// primary: '#000000',
|
|
47
|
+
// secondary: '#595959',
|
|
48
|
+
// dataContrast: ['#FF4D4F', '#FAAD14', '#52C41A', '#1890FF', '#722ED1', '#EB2F96', '#13C2C2', '#FA541C', '#2F54EB', '#A0D911'],
|
|
49
|
+
// background: '#FFFFFF'
|
|
50
|
+
// },
|
|
51
|
+
// dark: {
|
|
52
|
+
// data: ['#5B8FF9', '#61DDAA', '#65789B', '#F6BD16', '#7262FD', '#78D3F8', '#9661BC', '#F6903D', '#008685', '#F08BB4'],
|
|
53
|
+
// primary: '#FFFFFF',
|
|
54
|
+
// secondary: '#BFBFBF',
|
|
55
|
+
// dataContrast: ['#FF4D4F', '#FAAD14', '#52C41A', '#1890FF', '#722ED1', '#EB2F96', '#13C2C2', '#FA541C', '#2F54EB', '#A0D911'],
|
|
56
|
+
// background: '#1F1F1F'
|
|
57
|
+
// }
|
|
58
|
+
// },
|
|
59
|
+
// fontSize: '12px'
|
|
60
|
+
colorScheme: 'light',
|
|
61
|
+
colors: {
|
|
62
|
+
light: {
|
|
63
|
+
data: [
|
|
64
|
+
"#0088FF",
|
|
65
|
+
"#FF3232",
|
|
66
|
+
"#38BEA8",
|
|
67
|
+
"#6F3BD5",
|
|
68
|
+
"#314285",
|
|
69
|
+
"#42C724",
|
|
70
|
+
"#D52580",
|
|
71
|
+
"#F4721B",
|
|
72
|
+
"#D117EA",
|
|
73
|
+
"#7E7D7D"
|
|
74
|
+
],
|
|
75
|
+
primary: '#000000',
|
|
76
|
+
secondary: '#e0e0e0',
|
|
77
|
+
dataContrast: ['#ffffff', '#000000'],
|
|
78
|
+
background: '#FFFFFF'
|
|
79
|
+
},
|
|
80
|
+
dark: {
|
|
81
|
+
data: [
|
|
82
|
+
"#4BABFF",
|
|
83
|
+
"#FF6C6C",
|
|
84
|
+
"#7DD3C4",
|
|
85
|
+
"#8E6BC9",
|
|
86
|
+
"#5366AC",
|
|
87
|
+
"#86DC72",
|
|
88
|
+
"#FF72BB",
|
|
89
|
+
"#F9B052",
|
|
90
|
+
"#EF76FF",
|
|
91
|
+
"#C4C4C4"
|
|
92
|
+
],
|
|
93
|
+
primary: '#FFFFFF',
|
|
94
|
+
secondary: '#e0e0e0',
|
|
95
|
+
dataContrast: ['#ffffff', '#000000'],
|
|
96
|
+
background: '#000000'
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
fontSize: '0.875rem'
|
|
100
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ChartContext, DefineLayerConfig, LayerEntity, ExtendableContext, LayerEnableProps } from "./types"
|
|
2
|
+
import { createLayer } from "./layer/createLayer"
|
|
3
|
+
|
|
4
|
+
export const defineCanvasLayer = <
|
|
5
|
+
ExtendContext extends ExtendableContext,
|
|
6
|
+
PluginParams extends Record<string, any>,
|
|
7
|
+
LayerParams extends Record<string, any>,
|
|
8
|
+
>(config: DefineLayerConfig<'canvas', ExtendContext, PluginParams, LayerParams>) => {
|
|
9
|
+
return class Layer implements LayerEntity<ExtendContext, PluginParams, LayerParams> {
|
|
10
|
+
_name: string
|
|
11
|
+
_defaultParams: LayerParams
|
|
12
|
+
_layerIndex: number
|
|
13
|
+
_initShow: boolean
|
|
14
|
+
_enable: (enableProps: LayerEnableProps<'canvas', ExtendContext, PluginParams, LayerParams>) => void
|
|
15
|
+
_disable: () => void
|
|
16
|
+
_updateParams: (params: Partial<LayerParams>) => void
|
|
17
|
+
_forceReplaceParams: (params: LayerParams) => void
|
|
18
|
+
_getParams: () => Readonly<LayerParams>
|
|
19
|
+
_destroy: () => void
|
|
20
|
+
constructor () {
|
|
21
|
+
return createLayer<ExtendContext, PluginParams, LayerParams>('canvas', config)
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { DeepPartial, DefinePluginConfig, PluginEntity, ChartContext, ExtendableContext } from './types'
|
|
2
|
+
import { createPlugin } from './plugin/createPlugin'
|
|
3
|
+
|
|
4
|
+
export const defineCanvasPlugin = <
|
|
5
|
+
ExtendContext extends ExtendableContext,
|
|
6
|
+
PluginParams extends Record<string, any>,
|
|
7
|
+
AllLayerParams extends Record<string, any>,
|
|
8
|
+
>(config: DefinePluginConfig<ExtendContext, PluginParams, AllLayerParams>) => {
|
|
9
|
+
return class Plugin implements PluginEntity<'canvas', PluginParams, AllLayerParams> {
|
|
10
|
+
_name: string
|
|
11
|
+
_elementType: 'canvas'
|
|
12
|
+
_getId: () => string
|
|
13
|
+
_setId: (id: string) => void
|
|
14
|
+
_injectContext: (context: ChartContext<{}>) => void
|
|
15
|
+
show: (names: (keyof AllLayerParams) | (keyof AllLayerParams)[]) => void
|
|
16
|
+
showOnly: (names: (keyof AllLayerParams) | (keyof AllLayerParams)[]) => void
|
|
17
|
+
showAll: () => void
|
|
18
|
+
hide: (names: (keyof AllLayerParams) | (keyof AllLayerParams)[]) => void
|
|
19
|
+
hideAll: () => void
|
|
20
|
+
toggle: (names: (keyof AllLayerParams) | (keyof AllLayerParams)[]) => void
|
|
21
|
+
// setLayers: (partial: DeepPartial<PluginParams>) => void
|
|
22
|
+
getShownLayerNames: () => (keyof AllLayerParams)[]
|
|
23
|
+
updateParams: (patch: DeepPartial<PluginParams | AllLayerParams>) => void
|
|
24
|
+
forceReplaceParams: (full: PluginParams | AllLayerParams) => void
|
|
25
|
+
getParams: () => Readonly<PluginParams | AllLayerParams>
|
|
26
|
+
// layer: <LayerName extends keyof PluginParams>(name: LayerName) => {
|
|
27
|
+
// // set: (partial: DeepPartial<PluginParams[LayerName]>) => void
|
|
28
|
+
// update: (patch: DeepPartial<PluginParams[LayerName]>) => void
|
|
29
|
+
// replace: (full: PluginParams[LayerName]) => void
|
|
30
|
+
// show: () => void
|
|
31
|
+
// hide: () => void
|
|
32
|
+
// toggle: () => void
|
|
33
|
+
// }
|
|
34
|
+
destroy: () => void
|
|
35
|
+
constructor (params?: DeepPartial<PluginParams | AllLayerParams>) {
|
|
36
|
+
return createPlugin('canvas', config, params)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { ChartContext, DefineLayerConfig, LayerEntity, ExtendableContext, LayerEnableProps } from "./types"
|
|
2
|
+
import { createLayer } from "./layer/createLayer"
|
|
3
|
+
|
|
4
|
+
export const defineSVGLayer = <
|
|
5
|
+
ExtendContext extends ExtendableContext,
|
|
6
|
+
PluginParams extends Record<string, any>,
|
|
7
|
+
LayerParams extends Record<string, any>,
|
|
8
|
+
>(config: DefineLayerConfig<'svg', ExtendContext, PluginParams, LayerParams>) => {
|
|
9
|
+
return class Layer implements LayerEntity<ExtendContext, PluginParams, LayerParams> {
|
|
10
|
+
_name: string
|
|
11
|
+
_defaultParams: LayerParams
|
|
12
|
+
_layerIndex: number
|
|
13
|
+
_initShow: boolean
|
|
14
|
+
_enable: (enableProps: LayerEnableProps<'svg', ExtendContext, PluginParams, LayerParams>) => void
|
|
15
|
+
_disable: () => void
|
|
16
|
+
_updateParams: (params: Partial<LayerParams>) => void
|
|
17
|
+
_forceReplaceParams: (params: LayerParams) => void
|
|
18
|
+
_getParams: () => Readonly<LayerParams>
|
|
19
|
+
_destroy: () => void
|
|
20
|
+
constructor () {
|
|
21
|
+
return createLayer<ExtendContext, PluginParams, LayerParams>('svg', config)
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { DeepPartial, DefinePluginConfig, PluginEntity, ChartContext, ExtendableContext } from './types'
|
|
2
|
+
import { createPlugin } from './plugin/createPlugin'
|
|
3
|
+
|
|
4
|
+
export const defineSVGPlugin = <
|
|
5
|
+
ExtendContext extends ExtendableContext,
|
|
6
|
+
PluginParams extends Record<string, any>,
|
|
7
|
+
AllLayerParams extends Record<string, any>,
|
|
8
|
+
>(config: DefinePluginConfig<ExtendContext, PluginParams, AllLayerParams>) => {
|
|
9
|
+
return class Plugin implements PluginEntity<'svg', PluginParams, AllLayerParams> {
|
|
10
|
+
_name: string
|
|
11
|
+
_elementType: 'svg'
|
|
12
|
+
_getId: () => string
|
|
13
|
+
_setId: (id: string) => void
|
|
14
|
+
_injectContext: (context: ChartContext<{}>) => void
|
|
15
|
+
show: (names: (keyof AllLayerParams) | (keyof AllLayerParams)[]) => void
|
|
16
|
+
showOnly: (names: (keyof AllLayerParams) | (keyof AllLayerParams)[]) => void
|
|
17
|
+
showAll: () => void
|
|
18
|
+
hide: (names: (keyof AllLayerParams) | (keyof AllLayerParams)[]) => void
|
|
19
|
+
hideAll: () => void
|
|
20
|
+
toggle: (names: (keyof AllLayerParams) | (keyof AllLayerParams)[]) => void
|
|
21
|
+
// setLayers: (partial: DeepPartial<PluginParams>) => void
|
|
22
|
+
getShownLayerNames: () => (keyof AllLayerParams)[]
|
|
23
|
+
updateParams: (patch: DeepPartial<PluginParams | AllLayerParams>) => void
|
|
24
|
+
forceReplaceParams: (full: PluginParams | AllLayerParams) => void
|
|
25
|
+
getParams: () => Readonly<PluginParams | AllLayerParams>
|
|
26
|
+
// layer: <LayerName extends keyof PluginParams>(name: LayerName) => {
|
|
27
|
+
// // set: (partial: DeepPartial<PluginParams[LayerName]>) => void
|
|
28
|
+
// update: (patch: DeepPartial<PluginParams[LayerName]>) => void
|
|
29
|
+
// replace: (full: PluginParams[LayerName]) => void
|
|
30
|
+
// show: () => void
|
|
31
|
+
// hide: () => void
|
|
32
|
+
// toggle: () => void
|
|
33
|
+
// }
|
|
34
|
+
destroy: () => void
|
|
35
|
+
constructor (params?: DeepPartial<PluginParams | AllLayerParams>) {
|
|
36
|
+
return createPlugin('svg', config, params)
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,20 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
export {
|
|
3
|
-
export {
|
|
4
|
-
export {
|
|
5
|
-
export {
|
|
6
|
-
export {
|
|
7
|
-
export
|
|
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 './utils'
|
|
18
|
-
export * from './defaults'
|
|
19
|
-
|
|
20
|
-
|
|
1
|
+
|
|
2
|
+
export { OrbCharts } from './OrbCharts'
|
|
3
|
+
export { defineSVGPlugin } from './defineSVGPlugin'
|
|
4
|
+
export { defineSVGLayer } from './defineSVGLayer'
|
|
5
|
+
export { defineCanvasPlugin } from './defineCanvasPlugin'
|
|
6
|
+
export { defineCanvasLayer } from './defineCanvasLayer'
|
|
7
|
+
export * from './types'
|
|
8
|
+
export * from './utils'
|