@orbcharts/core 3.0.7 → 4.0.0-beta.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 +2849 -6594
- 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 +24 -13
- package/src/OrbCharts.ts +35 -0
- package/src/chart/createChart.ts +1014 -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 +120 -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 +488 -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 +101 -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 -307
- 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,1014 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Subject,
|
|
3
|
+
BehaviorSubject,
|
|
4
|
+
Observable,
|
|
5
|
+
EMPTY,
|
|
6
|
+
shareReplay,
|
|
7
|
+
takeUntil,
|
|
8
|
+
filter,
|
|
9
|
+
switchMap,
|
|
10
|
+
of,
|
|
11
|
+
iif,
|
|
12
|
+
map,
|
|
13
|
+
debounceTime,
|
|
14
|
+
throttleTime,
|
|
15
|
+
distinctUntilChanged,
|
|
16
|
+
mergeWith,
|
|
17
|
+
share,
|
|
18
|
+
combineLatest,
|
|
19
|
+
catchError
|
|
20
|
+
} from 'rxjs'
|
|
21
|
+
import type {
|
|
22
|
+
DeepPartial,
|
|
23
|
+
CreateChart,
|
|
24
|
+
PartialChartOptions,
|
|
25
|
+
ChartContext,
|
|
26
|
+
RawData,
|
|
27
|
+
Encoding,
|
|
28
|
+
ModelData,
|
|
29
|
+
PluginInfo,
|
|
30
|
+
PluginEntity,
|
|
31
|
+
Theme,
|
|
32
|
+
EventData,
|
|
33
|
+
Size,
|
|
34
|
+
SizeConfig,
|
|
35
|
+
ValidatorResult,
|
|
36
|
+
LayerInfo,
|
|
37
|
+
} from '../types'
|
|
38
|
+
import {
|
|
39
|
+
isDom,
|
|
40
|
+
isPlainObject,
|
|
41
|
+
removeElementChildren,
|
|
42
|
+
deepOverwrite,
|
|
43
|
+
validateObject,
|
|
44
|
+
createValidatorErrorMessage,
|
|
45
|
+
createValidatorWarningMessage,
|
|
46
|
+
createOrbChartsErrorMessage,
|
|
47
|
+
resizeObservable,
|
|
48
|
+
createUnexpectedErrorMessage,
|
|
49
|
+
createSVG,
|
|
50
|
+
createSVGGroup,
|
|
51
|
+
createCanvas
|
|
52
|
+
} from '../utils'
|
|
53
|
+
import { DEFAULT_DATA_ENCODING, DEFAULT_THEME, DEFAULT_SIZE_CONFIG } from './defaults'
|
|
54
|
+
import { createSeriesData } from './createSeriesData'
|
|
55
|
+
import { createGridData } from './createGridData'
|
|
56
|
+
import { createMultivariateData } from './createMultivariateData'
|
|
57
|
+
import { createGraphData } from './createGraphData'
|
|
58
|
+
import { createTreeData } from './createTreeData'
|
|
59
|
+
import { handleElementLifecycle } from '../utils/dom-lifecycle'
|
|
60
|
+
import { createLayerClassName } from '../utils/orbchartsUtils'
|
|
61
|
+
|
|
62
|
+
interface LayerMakerInfo extends LayerInfo {
|
|
63
|
+
pluginSeq: number
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function elementValidator (element: HTMLElement | Element): ValidatorResult {
|
|
67
|
+
const result = validateObject({ element }, {
|
|
68
|
+
element: {
|
|
69
|
+
toBe: 'Dom',
|
|
70
|
+
test: (value: any) => isDom(value)
|
|
71
|
+
},
|
|
72
|
+
})
|
|
73
|
+
|
|
74
|
+
return result
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function sizeOptionsValidator (sizeConfig: DeepPartial<SizeConfig>): ValidatorResult {
|
|
78
|
+
const result = validateObject(sizeConfig, {
|
|
79
|
+
width: {
|
|
80
|
+
toBe: '"auto" | number',
|
|
81
|
+
test: (value: any) => value === 'auto' || typeof value === 'number'
|
|
82
|
+
},
|
|
83
|
+
height: {
|
|
84
|
+
toBe: '"auto" | number',
|
|
85
|
+
test: (value: any) => value === 'auto' || typeof value === 'number'
|
|
86
|
+
},
|
|
87
|
+
resizeDebounce: {
|
|
88
|
+
toBe: 'number',
|
|
89
|
+
test: (value: any) => typeof value === 'number'
|
|
90
|
+
}
|
|
91
|
+
})
|
|
92
|
+
return result
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
function themeOptionsValidator (themeConfig: DeepPartial<Theme>): ValidatorResult {
|
|
96
|
+
const result = validateObject(themeConfig, {
|
|
97
|
+
colorScheme: {
|
|
98
|
+
toBe: '"dark" | "light" | "auto"',
|
|
99
|
+
test: (value: any) => value === 'dark' || value === 'light' || value === 'auto'
|
|
100
|
+
},
|
|
101
|
+
colors: {
|
|
102
|
+
toBeTypes: ['object'],
|
|
103
|
+
test: (value: any) => {
|
|
104
|
+
return value.light && value.dark
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
fontSize: {
|
|
108
|
+
toBeTypes: ['string'],
|
|
109
|
+
},
|
|
110
|
+
})
|
|
111
|
+
if (themeConfig.colors) {
|
|
112
|
+
const colorsResult = validateObject(themeConfig.colors, {
|
|
113
|
+
light: {
|
|
114
|
+
toBeTypes: ['object'],
|
|
115
|
+
test: (value: any) => {
|
|
116
|
+
return value.data && value.primary && value.secondary && value.dataContrast && value.background
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
dark: {
|
|
120
|
+
toBeTypes: ['object'],
|
|
121
|
+
test: (value: any) => {
|
|
122
|
+
return value.data && value.primary && value.secondary && value.dataContrast && value.background
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
})
|
|
126
|
+
if (colorsResult.status === 'error') {
|
|
127
|
+
return colorsResult
|
|
128
|
+
}
|
|
129
|
+
if (themeConfig.colors.light) {
|
|
130
|
+
const lightColorsResult = validateObject(themeConfig.colors.light, {
|
|
131
|
+
data: {
|
|
132
|
+
toBeTypes: ['string[]']
|
|
133
|
+
},
|
|
134
|
+
primary: {
|
|
135
|
+
toBeTypes: ['string']
|
|
136
|
+
},
|
|
137
|
+
secondary: {
|
|
138
|
+
toBeTypes: ['string']
|
|
139
|
+
},
|
|
140
|
+
dataContrast: {
|
|
141
|
+
toBeTypes: ['string[]']
|
|
142
|
+
},
|
|
143
|
+
background: {
|
|
144
|
+
toBeTypes: ['string']
|
|
145
|
+
}
|
|
146
|
+
})
|
|
147
|
+
if (lightColorsResult.status === 'error') {
|
|
148
|
+
return lightColorsResult
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
if (themeConfig.colors.dark) {
|
|
152
|
+
const darkColorsResult = validateObject(themeConfig.colors.dark, {
|
|
153
|
+
data: {
|
|
154
|
+
toBeTypes: ['string[]']
|
|
155
|
+
},
|
|
156
|
+
primary: {
|
|
157
|
+
toBeTypes: ['string']
|
|
158
|
+
},
|
|
159
|
+
secondary: {
|
|
160
|
+
toBeTypes: ['string']
|
|
161
|
+
},
|
|
162
|
+
dataContrast: {
|
|
163
|
+
toBeTypes: ['string[]']
|
|
164
|
+
},
|
|
165
|
+
background: {
|
|
166
|
+
toBeTypes: ['string']
|
|
167
|
+
}
|
|
168
|
+
})
|
|
169
|
+
if (darkColorsResult.status === 'error') {
|
|
170
|
+
return darkColorsResult
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
return result
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function encodingOptionsValidator (encodingConfig: DeepPartial<Encoding>): ValidatorResult {
|
|
178
|
+
const result = validateObject(encodingConfig, {
|
|
179
|
+
dataset: {
|
|
180
|
+
toBeTypes: ['object']
|
|
181
|
+
},
|
|
182
|
+
series: {
|
|
183
|
+
toBeTypes: ['object']
|
|
184
|
+
},
|
|
185
|
+
category: {
|
|
186
|
+
toBeTypes: ['object']
|
|
187
|
+
},
|
|
188
|
+
value: {
|
|
189
|
+
toBeTypes: ['object']
|
|
190
|
+
},
|
|
191
|
+
multivariate: {
|
|
192
|
+
toBeTypes: ['object']
|
|
193
|
+
},
|
|
194
|
+
color: {
|
|
195
|
+
toBeTypes: ['object']
|
|
196
|
+
}
|
|
197
|
+
})
|
|
198
|
+
if (encodingConfig.dataset) {
|
|
199
|
+
const datasetResult = validateObject(encodingConfig.dataset, {
|
|
200
|
+
from: {
|
|
201
|
+
toBeTypes: ['string']
|
|
202
|
+
},
|
|
203
|
+
sort: {
|
|
204
|
+
toBe: '"original" | "alphabetical" | string[]',
|
|
205
|
+
test: (value: any) => value === 'original' || value === 'alphabetical' || (Array.isArray(value) && value.every((v) => typeof v === 'string'))
|
|
206
|
+
}
|
|
207
|
+
})
|
|
208
|
+
if (datasetResult.status === 'error') {
|
|
209
|
+
return datasetResult
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
if (encodingConfig.series) {
|
|
213
|
+
const seriesResult = validateObject(encodingConfig.series, {
|
|
214
|
+
from: {
|
|
215
|
+
toBeTypes: ['string']
|
|
216
|
+
},
|
|
217
|
+
sort: {
|
|
218
|
+
toBe: '"original" | "alphabetical" | string[]',
|
|
219
|
+
test: (value: any) => value === 'original' || value === 'alphabetical' || (Array.isArray(value) && value.every((v) => typeof v === 'string'))
|
|
220
|
+
}
|
|
221
|
+
})
|
|
222
|
+
if (seriesResult.status === 'error') {
|
|
223
|
+
return seriesResult
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
if (encodingConfig.category) {
|
|
227
|
+
const categoryResult = validateObject(encodingConfig.category, {
|
|
228
|
+
from: {
|
|
229
|
+
toBeTypes: ['string']
|
|
230
|
+
},
|
|
231
|
+
sort: {
|
|
232
|
+
toBe: '"original" | "alphabetical" | string[]',
|
|
233
|
+
test: (value: any) => value === 'original' || value === 'alphabetical' || (Array.isArray(value) && value.every((v) => typeof v === 'string'))
|
|
234
|
+
}
|
|
235
|
+
})
|
|
236
|
+
if (categoryResult.status === 'error') {
|
|
237
|
+
return categoryResult
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
if (encodingConfig.value) {
|
|
241
|
+
const valueResult = validateObject(encodingConfig.value, {
|
|
242
|
+
from: {
|
|
243
|
+
toBeTypes: ['string']
|
|
244
|
+
},
|
|
245
|
+
sort: {
|
|
246
|
+
toBe: '"original" | "asc" | "desc"',
|
|
247
|
+
test: (value: any) => value === 'original' || value === 'asc' || value === 'desc'
|
|
248
|
+
},
|
|
249
|
+
aggregate: {
|
|
250
|
+
toBe: '"sum" | "mean" | "median" | "min" | "max" | "count" | "none"',
|
|
251
|
+
test: (value: any) => ['sum', 'mean', 'median', 'min', 'max', 'count', 'none'].includes(value)
|
|
252
|
+
}
|
|
253
|
+
})
|
|
254
|
+
if (valueResult.status === 'error') {
|
|
255
|
+
return valueResult
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
if (encodingConfig.multivariate) {
|
|
259
|
+
const multivariateResult = validateObject({ multivariate: encodingConfig.multivariate}, {
|
|
260
|
+
multivariate: {
|
|
261
|
+
toBe: 'EncodingMultivariateItem[]',
|
|
262
|
+
test: (value: any) => Array.isArray(value)
|
|
263
|
+
&& value.every((v) => typeof v.from === 'string' && typeof v.label === 'string')
|
|
264
|
+
}
|
|
265
|
+
})
|
|
266
|
+
if (multivariateResult.status === 'error') {
|
|
267
|
+
return multivariateResult
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
if (encodingConfig.color) {
|
|
271
|
+
const colorResult = validateObject(encodingConfig.color, {
|
|
272
|
+
from: {
|
|
273
|
+
toBe: '"index" | "series" | "category" | "dataset"',
|
|
274
|
+
test: (value: any) => ['index', 'series', 'category', 'dataset'].includes(value)
|
|
275
|
+
}
|
|
276
|
+
})
|
|
277
|
+
if (colorResult.status === 'error') {
|
|
278
|
+
return colorResult
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
return result
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
function dataValidator (data: RawData): ValidatorResult {
|
|
285
|
+
// 先檢查 data 的基本格式
|
|
286
|
+
const result = validateObject({ data }, {
|
|
287
|
+
data: {
|
|
288
|
+
toBe: 'RawDataColumn[] | RawDataColumn[][]',
|
|
289
|
+
// 畢免資料量過大檢查不完,不深度檢查
|
|
290
|
+
test: (value) => Array.isArray(value) && value.every((d) => Array.isArray(d) || isPlainObject(d))
|
|
291
|
+
}
|
|
292
|
+
})
|
|
293
|
+
return result
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function pluginsValidator (plugins: PluginEntity<any, any, any>[]): ValidatorResult {
|
|
297
|
+
const result = validateObject({ plugins }, {
|
|
298
|
+
plugins: {
|
|
299
|
+
toBe: `PluginEntity[]`,
|
|
300
|
+
test: (value: PluginEntity<any, any, any>[]) => {
|
|
301
|
+
return Array.isArray(value)
|
|
302
|
+
&& value.every((v) => isPlainObject(v) && typeof v._name === 'string' && typeof v._injectContext === 'function')
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
})
|
|
306
|
+
return result
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
function chartOptionsValidator (chartOptionsPartial: PartialChartOptions): ValidatorResult {
|
|
310
|
+
if (!chartOptionsPartial) {
|
|
311
|
+
// chartOptions 可為空值
|
|
312
|
+
return { status: 'success', columnName: '', expectToBe: '' }
|
|
313
|
+
}
|
|
314
|
+
const result = validateObject(chartOptionsPartial, {
|
|
315
|
+
size: {
|
|
316
|
+
toBeTypes: ['object']
|
|
317
|
+
},
|
|
318
|
+
theme: {
|
|
319
|
+
toBeTypes: ['object']
|
|
320
|
+
},
|
|
321
|
+
data: {
|
|
322
|
+
toBe: 'RawDataColumn[] | RawDataColumn[][]',
|
|
323
|
+
// 畢免資料量過大檢查不完,不深度檢查
|
|
324
|
+
test: (value) => Array.isArray(value)
|
|
325
|
+
},
|
|
326
|
+
encoding: {
|
|
327
|
+
toBeTypes: ['object']
|
|
328
|
+
},
|
|
329
|
+
plugins: {
|
|
330
|
+
toBe: `PluginEntity[]`,
|
|
331
|
+
test: (value: PluginEntity<any, any, any>[]) => {
|
|
332
|
+
return Array.isArray(value)
|
|
333
|
+
&& value.every((v) => isPlainObject(v) && typeof v._name === 'string' && typeof v._injectContext === 'function')
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
})
|
|
337
|
+
if (result.status === 'error') {
|
|
338
|
+
return result
|
|
339
|
+
}
|
|
340
|
+
if (chartOptionsPartial.size) {
|
|
341
|
+
const sizeResult = sizeOptionsValidator(chartOptionsPartial.size)
|
|
342
|
+
if (sizeResult.status === 'error') {
|
|
343
|
+
return sizeResult
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
if (chartOptionsPartial.theme) {
|
|
347
|
+
const themeResult = themeOptionsValidator(chartOptionsPartial.theme)
|
|
348
|
+
if (themeResult.status === 'error') {
|
|
349
|
+
return themeResult
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
if (chartOptionsPartial.encoding) {
|
|
353
|
+
const encodingResult = encodingOptionsValidator(chartOptionsPartial.encoding)
|
|
354
|
+
if (encodingResult.status === 'error') {
|
|
355
|
+
return encodingResult
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
if (chartOptionsPartial.data) {
|
|
359
|
+
const dataResult = dataValidator(chartOptionsPartial.data)
|
|
360
|
+
if (dataResult.status === 'error') {
|
|
361
|
+
return dataResult
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
if (chartOptionsPartial.plugins) {
|
|
365
|
+
const pluginsResult = pluginsValidator(chartOptionsPartial.plugins)
|
|
366
|
+
if (pluginsResult.status === 'error') {
|
|
367
|
+
return pluginsResult
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
return result
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
function createLayerElementsUpdater ({ svgElement$, canvasElement$, pluginsInstance$ }:{
|
|
374
|
+
svgElement$: BehaviorSubject<SVGElement | null>
|
|
375
|
+
canvasElement$: BehaviorSubject<HTMLCanvasElement | null>
|
|
376
|
+
pluginsInstance$: BehaviorSubject<PluginEntity<any, unknown, unknown>[]>
|
|
377
|
+
}) {
|
|
378
|
+
// 紀錄 layer 陣列的資訊
|
|
379
|
+
let svgLayerInfo: LayerMakerInfo[] = []
|
|
380
|
+
let canvasLayerInfo: LayerMakerInfo[] = []
|
|
381
|
+
// 產生新的 layer 資訊
|
|
382
|
+
const createLayerInfo = (elementType: 'svg' | 'canvas', fromPluginId: string, fetchLayerInfo: LayerInfo[]) => {
|
|
383
|
+
const pluginSeq = pluginsInstance$.getValue().reduce((acc, plugin, index) => {
|
|
384
|
+
acc[plugin._getId()] = index
|
|
385
|
+
return acc
|
|
386
|
+
}, {} as Record<string, number>)
|
|
387
|
+
const addedLayerInfo: LayerMakerInfo[] = fetchLayerInfo.map(layerInfo => ({
|
|
388
|
+
...layerInfo,
|
|
389
|
+
pluginSeq: pluginSeq[layerInfo.pluginId]
|
|
390
|
+
}))
|
|
391
|
+
const newLayerInfo = (elementType === 'svg' ? svgLayerInfo : canvasLayerInfo)
|
|
392
|
+
.filter((layerInfo) => layerInfo.pluginId !== fromPluginId)
|
|
393
|
+
.map(layerInfo => {
|
|
394
|
+
return {
|
|
395
|
+
...layerInfo,
|
|
396
|
+
pluginSeq: pluginSeq[layerInfo.pluginId]
|
|
397
|
+
}
|
|
398
|
+
})
|
|
399
|
+
.concat(addedLayerInfo)
|
|
400
|
+
.sort((a, b) => {
|
|
401
|
+
// 優先排layerIndex再排pluginSeq
|
|
402
|
+
return a.layerIndex - b.layerIndex || a.pluginSeq - b.pluginSeq
|
|
403
|
+
})
|
|
404
|
+
return newLayerInfo
|
|
405
|
+
}
|
|
406
|
+
// layer elements
|
|
407
|
+
const layerSVGElementsRef: Record<string, SVGGElement> = {}
|
|
408
|
+
const layerCanvasElementsRef: Record<string, HTMLCanvasElement> = {}
|
|
409
|
+
|
|
410
|
+
return <ElementType extends 'svg' | 'canvas'>(elementType: ElementType, fromPluginId: string, fetchLayerInfo: LayerInfo[])
|
|
411
|
+
: Record<string, ElementType extends 'svg' ? SVGGElement : HTMLCanvasElement> => {
|
|
412
|
+
|
|
413
|
+
const element = elementType === 'svg' ? svgElement$.getValue() : canvasElement$.getValue()
|
|
414
|
+
|
|
415
|
+
if (!element) {
|
|
416
|
+
return {}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
const layerElementsRef = elementType === 'svg' ? layerSVGElementsRef : layerCanvasElementsRef
|
|
420
|
+
const newLayerInfo = createLayerInfo(elementType, fromPluginId, fetchLayerInfo)
|
|
421
|
+
const sortedLayerClassNames = newLayerInfo.map(info => createLayerClassName(info.pluginId, info.layerName))
|
|
422
|
+
|
|
423
|
+
// 根據新的 layerInfo 來更新 DOM 結構
|
|
424
|
+
if (elementType === 'svg') {
|
|
425
|
+
handleElementLifecycle(
|
|
426
|
+
element,
|
|
427
|
+
sortedLayerClassNames,
|
|
428
|
+
layerElementsRef as Record<string, SVGGElement>,
|
|
429
|
+
(elementClassName) => createSVGGroup(elementClassName)
|
|
430
|
+
)
|
|
431
|
+
svgLayerInfo = newLayerInfo
|
|
432
|
+
} else {
|
|
433
|
+
handleElementLifecycle(
|
|
434
|
+
element,
|
|
435
|
+
sortedLayerClassNames,
|
|
436
|
+
layerElementsRef as Record<string, HTMLCanvasElement>,
|
|
437
|
+
(elementClassName) => createCanvas(elementClassName)
|
|
438
|
+
)
|
|
439
|
+
canvasLayerInfo = newLayerInfo
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// 回傳目前pluginId的elements (Record<layerName, element>)
|
|
443
|
+
const currentLayerElementsRef = Object.entries(layerElementsRef as Record<string, ElementType extends 'svg' ? SVGGElement : HTMLCanvasElement>)
|
|
444
|
+
.reduce((prev, [key, layerElement]) => {
|
|
445
|
+
const [orbchartsPrefix, pluginId, layerName] = key.split('-')
|
|
446
|
+
if (pluginId === fromPluginId) {
|
|
447
|
+
prev[layerName] = layerElement
|
|
448
|
+
}
|
|
449
|
+
return prev
|
|
450
|
+
}, {} as Record<string, ElementType extends 'svg' ? SVGGElement : HTMLCanvasElement>)
|
|
451
|
+
|
|
452
|
+
return currentLayerElementsRef
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
export const createChart: CreateChart = (element, options) => {
|
|
457
|
+
try {
|
|
458
|
+
const { status, columnName, expectToBe } = chartOptionsValidator(options)
|
|
459
|
+
if (status === 'error') {
|
|
460
|
+
throw new Error(createValidatorErrorMessage({
|
|
461
|
+
columnName,
|
|
462
|
+
expectToBe,
|
|
463
|
+
from: 'Chart.constructor'
|
|
464
|
+
}))
|
|
465
|
+
} else if (status === 'warning') {
|
|
466
|
+
console.warn(createValidatorWarningMessage({
|
|
467
|
+
columnName,
|
|
468
|
+
expectToBe,
|
|
469
|
+
from: 'Chart.constructor'
|
|
470
|
+
}))
|
|
471
|
+
} else {
|
|
472
|
+
const { status, columnName, expectToBe } = elementValidator(element)
|
|
473
|
+
if (status === 'error') {
|
|
474
|
+
throw new Error(createValidatorErrorMessage({
|
|
475
|
+
columnName,
|
|
476
|
+
expectToBe,
|
|
477
|
+
from: 'Chart.constructor'
|
|
478
|
+
}))
|
|
479
|
+
} else if (status === 'warning') {
|
|
480
|
+
console.warn(createValidatorWarningMessage({
|
|
481
|
+
columnName,
|
|
482
|
+
expectToBe,
|
|
483
|
+
from: 'Chart.constructor'
|
|
484
|
+
}))
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
} catch (e) {
|
|
488
|
+
throw new Error(e)
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
const destroy$ = new Subject()
|
|
492
|
+
|
|
493
|
+
// data
|
|
494
|
+
const rawData$ = new BehaviorSubject<RawData>(options?.data || [])
|
|
495
|
+
// data encoding
|
|
496
|
+
const defaultEncoding = options && options.encoding
|
|
497
|
+
? deepOverwrite(DEFAULT_DATA_ENCODING, options.encoding)
|
|
498
|
+
: DEFAULT_DATA_ENCODING
|
|
499
|
+
const currentEncoding$ = new BehaviorSubject<Encoding>(defaultEncoding)
|
|
500
|
+
// theme
|
|
501
|
+
const defaultTheme = options && options.theme
|
|
502
|
+
? deepOverwrite(DEFAULT_THEME, options.theme)
|
|
503
|
+
: DEFAULT_THEME
|
|
504
|
+
// const defaultTheme$ = new BehaviorSubject<Theme>(defaultTheme)
|
|
505
|
+
// const previousTheme$ = new BehaviorSubject<Theme>(defaultTheme)
|
|
506
|
+
const currentTheme$ = new BehaviorSubject<Theme>(defaultTheme)
|
|
507
|
+
// plugins
|
|
508
|
+
const pluginsInstance$ = new BehaviorSubject<PluginEntity<'svg' | 'canvas', unknown, unknown>[]>(options?.plugins || [])
|
|
509
|
+
// size
|
|
510
|
+
const defaultSizeConfig = options && options.size
|
|
511
|
+
? deepOverwrite(DEFAULT_SIZE_CONFIG, options.size)
|
|
512
|
+
: DEFAULT_SIZE_CONFIG
|
|
513
|
+
const sizeConfig$ = new BehaviorSubject<SizeConfig>(defaultSizeConfig)
|
|
514
|
+
// elements
|
|
515
|
+
const svgElement$ = new BehaviorSubject<SVGElement | null>(null)
|
|
516
|
+
const canvasElement$ = new BehaviorSubject<HTMLCanvasElement | null>(null)
|
|
517
|
+
|
|
518
|
+
|
|
519
|
+
// chart context
|
|
520
|
+
const context: ChartContext<{}> = (() => {
|
|
521
|
+
// 監聽外層的element尺寸
|
|
522
|
+
const size$: Observable<Size> = sizeConfig$.pipe(
|
|
523
|
+
switchMap(sizeConfig => {
|
|
524
|
+
return iif(
|
|
525
|
+
() =>
|
|
526
|
+
sizeConfig.width === 'auto' || sizeConfig.height === 'auto',
|
|
527
|
+
// 有 'auto' 的話就監聽element的尺寸
|
|
528
|
+
resizeObservable(element).pipe(
|
|
529
|
+
map((d) => {
|
|
530
|
+
return {
|
|
531
|
+
width: sizeConfig.width === 'auto' ? d.width : sizeConfig.width,
|
|
532
|
+
height: sizeConfig.height === 'auto'
|
|
533
|
+
? (d.height <= 0 ? d.width : d.height) // html高度很容易出現0的狀況,為避免顯示不出來這種情況就和width相等
|
|
534
|
+
: sizeConfig.height
|
|
535
|
+
}
|
|
536
|
+
}),
|
|
537
|
+
debounceTime(sizeConfig.resizeDebounce),
|
|
538
|
+
distinctUntilChanged((a, b) => JSON.stringify(a) === JSON.stringify(b))
|
|
539
|
+
),
|
|
540
|
+
of({
|
|
541
|
+
width: sizeConfig.width as number,
|
|
542
|
+
height: sizeConfig.height as number
|
|
543
|
+
})
|
|
544
|
+
)
|
|
545
|
+
}),
|
|
546
|
+
takeUntil(destroy$),
|
|
547
|
+
shareReplay(1)
|
|
548
|
+
)
|
|
549
|
+
// const encoding$ = new Observable<Encoding>(subscriber => {
|
|
550
|
+
// currentEncoding$.subscribe(data => {
|
|
551
|
+
// subscriber.next(data)
|
|
552
|
+
// })
|
|
553
|
+
// }).pipe(
|
|
554
|
+
// shareReplay(1)
|
|
555
|
+
// )
|
|
556
|
+
const encoding$ = currentEncoding$
|
|
557
|
+
.asObservable()
|
|
558
|
+
.pipe(
|
|
559
|
+
shareReplay(1)
|
|
560
|
+
)
|
|
561
|
+
const seriesData$ = combineLatest([
|
|
562
|
+
rawData$,
|
|
563
|
+
currentEncoding$,
|
|
564
|
+
currentTheme$
|
|
565
|
+
]).pipe(
|
|
566
|
+
debounceTime(0),
|
|
567
|
+
map(([rawData, encoding, theme]) => {
|
|
568
|
+
try {
|
|
569
|
+
return createSeriesData(rawData, encoding, theme)
|
|
570
|
+
} catch (e) {
|
|
571
|
+
throw new Error(createUnexpectedErrorMessage({
|
|
572
|
+
from: 'Chart.seriesData$',
|
|
573
|
+
systemMessage: e
|
|
574
|
+
}))
|
|
575
|
+
}
|
|
576
|
+
}),
|
|
577
|
+
catchError((e) => {
|
|
578
|
+
console.error(createOrbChartsErrorMessage(e))
|
|
579
|
+
return EMPTY
|
|
580
|
+
}),
|
|
581
|
+
shareReplay(1)
|
|
582
|
+
)
|
|
583
|
+
const gridData$ = combineLatest([
|
|
584
|
+
rawData$,
|
|
585
|
+
currentEncoding$,
|
|
586
|
+
currentTheme$
|
|
587
|
+
]).pipe(
|
|
588
|
+
debounceTime(0),
|
|
589
|
+
map(([rawData, encoding, theme]) => {
|
|
590
|
+
try {
|
|
591
|
+
return createGridData(rawData, encoding, theme)
|
|
592
|
+
} catch (e) {
|
|
593
|
+
throw new Error(createUnexpectedErrorMessage({
|
|
594
|
+
from: 'Chart.gridData$',
|
|
595
|
+
systemMessage: e
|
|
596
|
+
}))
|
|
597
|
+
}
|
|
598
|
+
}),
|
|
599
|
+
catchError((e) => {
|
|
600
|
+
console.error(createOrbChartsErrorMessage(e))
|
|
601
|
+
return EMPTY
|
|
602
|
+
}),
|
|
603
|
+
shareReplay(1)
|
|
604
|
+
)
|
|
605
|
+
const multivariateData$ = combineLatest([
|
|
606
|
+
rawData$,
|
|
607
|
+
currentEncoding$,
|
|
608
|
+
currentTheme$
|
|
609
|
+
]).pipe(
|
|
610
|
+
debounceTime(0),
|
|
611
|
+
map(([rawData, encoding, theme]) => {
|
|
612
|
+
try {
|
|
613
|
+
return createMultivariateData(rawData, encoding, theme)
|
|
614
|
+
} catch (e) {
|
|
615
|
+
throw new Error(createUnexpectedErrorMessage({
|
|
616
|
+
from: 'Chart.multivariateData$',
|
|
617
|
+
systemMessage: e
|
|
618
|
+
}))
|
|
619
|
+
}
|
|
620
|
+
}),
|
|
621
|
+
catchError((e) => {
|
|
622
|
+
console.error(createOrbChartsErrorMessage(e))
|
|
623
|
+
return EMPTY
|
|
624
|
+
}),
|
|
625
|
+
shareReplay(1)
|
|
626
|
+
)
|
|
627
|
+
const graphData$ = combineLatest([
|
|
628
|
+
rawData$,
|
|
629
|
+
currentEncoding$,
|
|
630
|
+
currentTheme$
|
|
631
|
+
]).pipe(
|
|
632
|
+
debounceTime(0),
|
|
633
|
+
map(([rawData, encoding, theme]) => {
|
|
634
|
+
try {
|
|
635
|
+
return createGraphData(rawData, encoding, theme)
|
|
636
|
+
} catch (e) {
|
|
637
|
+
throw new Error(createUnexpectedErrorMessage({
|
|
638
|
+
from: 'Chart.graphData$',
|
|
639
|
+
systemMessage: e
|
|
640
|
+
}))
|
|
641
|
+
}
|
|
642
|
+
}),
|
|
643
|
+
catchError((e) => {
|
|
644
|
+
console.error(createOrbChartsErrorMessage(e))
|
|
645
|
+
return EMPTY
|
|
646
|
+
}),
|
|
647
|
+
shareReplay(1)
|
|
648
|
+
)
|
|
649
|
+
const treeData$ = combineLatest([
|
|
650
|
+
rawData$,
|
|
651
|
+
currentEncoding$,
|
|
652
|
+
currentTheme$
|
|
653
|
+
]).pipe(
|
|
654
|
+
debounceTime(0),
|
|
655
|
+
map(([rawData, encoding, theme]) => {
|
|
656
|
+
try {
|
|
657
|
+
return createTreeData(rawData, encoding, theme)
|
|
658
|
+
} catch (e) {
|
|
659
|
+
throw new Error(createUnexpectedErrorMessage({
|
|
660
|
+
from: 'Chart.treeData$',
|
|
661
|
+
systemMessage: e
|
|
662
|
+
}))
|
|
663
|
+
}
|
|
664
|
+
}),
|
|
665
|
+
catchError((e) => {
|
|
666
|
+
console.error(createOrbChartsErrorMessage(e))
|
|
667
|
+
return EMPTY
|
|
668
|
+
}),
|
|
669
|
+
shareReplay(1)
|
|
670
|
+
)
|
|
671
|
+
const plugins$ = new BehaviorSubject<PluginInfo[]>([])
|
|
672
|
+
pluginsInstance$.subscribe(plugins => {
|
|
673
|
+
const pluginIdRecord: Record<string, string> = {}
|
|
674
|
+
const pluginIds = plugins.map(plugin => {
|
|
675
|
+
let id = plugin._getId()
|
|
676
|
+
if (pluginIdRecord[id]) {
|
|
677
|
+
// 原本id加上序號
|
|
678
|
+
const prevNo = pluginIdRecord[id].split('[')[1]?.split(']')[0] || '0'
|
|
679
|
+
const newNo = parseInt(prevNo) + 1
|
|
680
|
+
id = `${id.split('[')[0]}[${newNo}]`
|
|
681
|
+
}
|
|
682
|
+
pluginIdRecord[id] = plugin._getId()
|
|
683
|
+
return id
|
|
684
|
+
})
|
|
685
|
+
// 更新 plugin 實例的 id(確保唯一性)
|
|
686
|
+
plugins.forEach((plugin, index) => plugin._setId(pluginIds[index]))
|
|
687
|
+
|
|
688
|
+
const pluginInfos = plugins.map(plugin => {
|
|
689
|
+
return {
|
|
690
|
+
id: plugin._getId(),
|
|
691
|
+
name: plugin._name,
|
|
692
|
+
elementType: plugin._elementType,
|
|
693
|
+
shownLayers: plugin.getShownLayerNames()
|
|
694
|
+
}
|
|
695
|
+
})
|
|
696
|
+
plugins$.next(pluginInfos)
|
|
697
|
+
})
|
|
698
|
+
// const theme$ = new Observable<Theme>(subscriber => {
|
|
699
|
+
// currentTheme$.subscribe(data => {
|
|
700
|
+
// subscriber.next(data)
|
|
701
|
+
// })
|
|
702
|
+
// })
|
|
703
|
+
const theme$ = currentTheme$
|
|
704
|
+
.asObservable()
|
|
705
|
+
.pipe(
|
|
706
|
+
shareReplay(1)
|
|
707
|
+
)
|
|
708
|
+
const eventTrigger$ = new Subject<EventData>()
|
|
709
|
+
|
|
710
|
+
// const event$ = new Observable<EventData>(subscriber => {
|
|
711
|
+
// eventTrigger$.subscribe((data) => {
|
|
712
|
+
// subscriber.next(data)
|
|
713
|
+
// })
|
|
714
|
+
// })
|
|
715
|
+
const event$ = eventTrigger$
|
|
716
|
+
.asObservable()
|
|
717
|
+
.pipe(
|
|
718
|
+
share()
|
|
719
|
+
)
|
|
720
|
+
const _context: ChartContext<{}> = {
|
|
721
|
+
root: element,
|
|
722
|
+
svg: null,
|
|
723
|
+
canvas: null,
|
|
724
|
+
size$: size$,
|
|
725
|
+
encoding$,
|
|
726
|
+
seriesData$,
|
|
727
|
+
gridData$,
|
|
728
|
+
multivariateData$,
|
|
729
|
+
graphData$,
|
|
730
|
+
treeData$,
|
|
731
|
+
plugins$,
|
|
732
|
+
theme$,
|
|
733
|
+
event$,
|
|
734
|
+
eventTrigger$,
|
|
735
|
+
_updateLayerElements: createLayerElementsUpdater({
|
|
736
|
+
svgElement$,
|
|
737
|
+
canvasElement$,
|
|
738
|
+
pluginsInstance$
|
|
739
|
+
})
|
|
740
|
+
}
|
|
741
|
+
return _context
|
|
742
|
+
})()
|
|
743
|
+
|
|
744
|
+
// OrbCharts 自有的 container(root 的直接子元素,由 OrbCharts 建立及擁有):
|
|
745
|
+
// svg/canvas 為 position:absolute,需要一個 positioned ancestor 作為錨點,
|
|
746
|
+
// 但 root 是使用者傳入的元素、不應修改它的樣式——
|
|
747
|
+
// 因此在 root 底下建立 position:relative 的 container 來掛載 svg/canvas。
|
|
748
|
+
// (container 本身高度為 0、不影響 root 的尺寸量測;destroy 時隨 root 清空一併移除)
|
|
749
|
+
let orbContainer: HTMLDivElement | null = null
|
|
750
|
+
const getOrbContainer = (): HTMLDivElement => {
|
|
751
|
+
if (!orbContainer) {
|
|
752
|
+
orbContainer = document.createElement('div')
|
|
753
|
+
orbContainer.classList.add('orbcharts-container')
|
|
754
|
+
orbContainer.style.position = 'relative'
|
|
755
|
+
element.appendChild(orbContainer)
|
|
756
|
+
}
|
|
757
|
+
return orbContainer
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
// inject context into plugins
|
|
761
|
+
pluginsInstance$.subscribe(plugins => {
|
|
762
|
+
// -- 建立頂層 svg 和 canvas --
|
|
763
|
+
let hasSVGPlugin = false
|
|
764
|
+
let hasCanvasPlugin = false
|
|
765
|
+
plugins.forEach(plugin => {
|
|
766
|
+
if (plugin._elementType === 'svg') {
|
|
767
|
+
hasSVGPlugin = true
|
|
768
|
+
} else if (plugin._elementType === 'canvas') {
|
|
769
|
+
hasCanvasPlugin = true
|
|
770
|
+
}
|
|
771
|
+
})
|
|
772
|
+
if (hasSVGPlugin && !context.svg) {
|
|
773
|
+
context.svg = createSVG('orbcharts-root-svg')
|
|
774
|
+
getOrbContainer().appendChild(context.svg)
|
|
775
|
+
svgElement$.next(context.svg)
|
|
776
|
+
} else if (!hasSVGPlugin && context.svg) {
|
|
777
|
+
context.svg.remove()
|
|
778
|
+
context.svg = null
|
|
779
|
+
svgElement$.next(null)
|
|
780
|
+
}
|
|
781
|
+
if (hasCanvasPlugin && !context.canvas) {
|
|
782
|
+
context.canvas = createCanvas('orbcharts-root-canvas')
|
|
783
|
+
getOrbContainer().appendChild(context.canvas)
|
|
784
|
+
canvasElement$.next(context.canvas)
|
|
785
|
+
} else if (!hasCanvasPlugin && context.canvas) {
|
|
786
|
+
context.canvas.remove()
|
|
787
|
+
context.canvas = null
|
|
788
|
+
canvasElement$.next(null)
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
// inject context
|
|
792
|
+
plugins.forEach(plugin => {
|
|
793
|
+
plugin._injectContext(context)
|
|
794
|
+
})
|
|
795
|
+
})
|
|
796
|
+
|
|
797
|
+
combineLatest({
|
|
798
|
+
size: context.size$,
|
|
799
|
+
svg: svgElement$,
|
|
800
|
+
}).pipe(
|
|
801
|
+
debounceTime(0),
|
|
802
|
+
filter(({ svg }) => !!svg)
|
|
803
|
+
).subscribe(({ size, svg }) => {
|
|
804
|
+
if (svg) {
|
|
805
|
+
svg.setAttribute('width', size.width.toString())
|
|
806
|
+
svg.setAttribute('height', size.height.toString())
|
|
807
|
+
}
|
|
808
|
+
})
|
|
809
|
+
|
|
810
|
+
combineLatest({
|
|
811
|
+
size: context.size$,
|
|
812
|
+
canvas: canvasElement$
|
|
813
|
+
}).pipe(
|
|
814
|
+
debounceTime(0),
|
|
815
|
+
filter(({ canvas }) => !!canvas)
|
|
816
|
+
).subscribe(({ size, canvas }) => {
|
|
817
|
+
if (canvas) {
|
|
818
|
+
canvas.width = size.width
|
|
819
|
+
canvas.height = size.height
|
|
820
|
+
}
|
|
821
|
+
})
|
|
822
|
+
|
|
823
|
+
// context.size$.subscribe(size => {
|
|
824
|
+
// const svgGElement: SVGGElement | null = context.root.querySelector(`:scope > svg`)
|
|
825
|
+
// const canvasElement: HTMLCanvasElement | null = context.root.querySelector(`:scope > canvas`)
|
|
826
|
+
// if (svgGElement) {
|
|
827
|
+
// svgGElement.setAttribute('width', size.width.toString())
|
|
828
|
+
// svgGElement.setAttribute('height', size.height.toString())
|
|
829
|
+
// }
|
|
830
|
+
// if (canvasElement) {
|
|
831
|
+
// canvasElement.width = size.width
|
|
832
|
+
// canvasElement.height = size.height
|
|
833
|
+
// }
|
|
834
|
+
// })
|
|
835
|
+
|
|
836
|
+
// create chart instance
|
|
837
|
+
return (() => {
|
|
838
|
+
function resize (sizeConfig: SizeConfig) {
|
|
839
|
+
sizeConfig$.next(sizeConfig)
|
|
840
|
+
}
|
|
841
|
+
function setData (data: RawData) {
|
|
842
|
+
try {
|
|
843
|
+
const { status, columnName, expectToBe } = dataValidator(data)
|
|
844
|
+
if (status === 'error') {
|
|
845
|
+
throw new Error(createValidatorErrorMessage({
|
|
846
|
+
columnName,
|
|
847
|
+
expectToBe,
|
|
848
|
+
from: 'Chart.setData'
|
|
849
|
+
}))
|
|
850
|
+
} else if (status === 'warning') {
|
|
851
|
+
console.warn(createValidatorWarningMessage({
|
|
852
|
+
columnName,
|
|
853
|
+
expectToBe,
|
|
854
|
+
from: 'Chart.setData'
|
|
855
|
+
}))
|
|
856
|
+
}
|
|
857
|
+
} catch (e) {
|
|
858
|
+
// 不中斷資料流
|
|
859
|
+
console.error(createOrbChartsErrorMessage(e))
|
|
860
|
+
}
|
|
861
|
+
rawData$.next(data)
|
|
862
|
+
}
|
|
863
|
+
// function setEncoding (partial: DeepPartial<Encoding>) {
|
|
864
|
+
// // deep-merge with default
|
|
865
|
+
// const currentEncoding = deepOverwrite(partial, defaultEncoding$.getValue())
|
|
866
|
+
// currentEncoding$.next(currentEncoding)
|
|
867
|
+
// }
|
|
868
|
+
function updateEncoding (patch: DeepPartial<Encoding>) {
|
|
869
|
+
try {
|
|
870
|
+
const { status, columnName, expectToBe } = encodingOptionsValidator(patch)
|
|
871
|
+
if (status === 'error') {
|
|
872
|
+
throw new Error(createValidatorErrorMessage({
|
|
873
|
+
columnName,
|
|
874
|
+
expectToBe,
|
|
875
|
+
from: 'Chart.updateEncoding'
|
|
876
|
+
}))
|
|
877
|
+
} else if (status === 'warning') {
|
|
878
|
+
console.warn(createValidatorWarningMessage({
|
|
879
|
+
columnName,
|
|
880
|
+
expectToBe,
|
|
881
|
+
from: 'Chart.updateEncoding'
|
|
882
|
+
}))
|
|
883
|
+
}
|
|
884
|
+
} catch (e) {
|
|
885
|
+
// 不中斷資料流
|
|
886
|
+
console.error(createOrbChartsErrorMessage(e))
|
|
887
|
+
}
|
|
888
|
+
// deep-merge with previous
|
|
889
|
+
const newEncoding = deepOverwrite(currentEncoding$.getValue(), patch)
|
|
890
|
+
currentEncoding$.next(newEncoding)
|
|
891
|
+
}
|
|
892
|
+
function forceReplaceEncoding (full: Encoding) {
|
|
893
|
+
// replace
|
|
894
|
+
currentEncoding$.next(full)
|
|
895
|
+
}
|
|
896
|
+
function getEncoding () {
|
|
897
|
+
return currentEncoding$.getValue()
|
|
898
|
+
}
|
|
899
|
+
function setPlugins (plugins: PluginEntity<any, unknown, unknown>[]) {
|
|
900
|
+
try {
|
|
901
|
+
const { status, columnName, expectToBe } = pluginsValidator(plugins)
|
|
902
|
+
if (status === 'error') {
|
|
903
|
+
throw new Error(createValidatorErrorMessage({
|
|
904
|
+
columnName,
|
|
905
|
+
expectToBe,
|
|
906
|
+
from: 'Chart.setPlugins'
|
|
907
|
+
}))
|
|
908
|
+
} else if (status === 'warning') {
|
|
909
|
+
console.warn(createValidatorWarningMessage({
|
|
910
|
+
columnName,
|
|
911
|
+
expectToBe,
|
|
912
|
+
from: 'Chart.setPlugins'
|
|
913
|
+
}))
|
|
914
|
+
}
|
|
915
|
+
} catch (e) {
|
|
916
|
+
// 不中斷資料流
|
|
917
|
+
console.error(createOrbChartsErrorMessage(e))
|
|
918
|
+
}
|
|
919
|
+
// replace all
|
|
920
|
+
pluginsInstance$.next(plugins)
|
|
921
|
+
}
|
|
922
|
+
function addPlugin (plugin: PluginEntity<any, unknown, unknown>) {
|
|
923
|
+
try {
|
|
924
|
+
const { status, columnName, expectToBe } = pluginsValidator([plugin])
|
|
925
|
+
if (status === 'error') {
|
|
926
|
+
throw new Error(createValidatorErrorMessage({
|
|
927
|
+
columnName,
|
|
928
|
+
expectToBe,
|
|
929
|
+
from: 'Chart.addPlugin'
|
|
930
|
+
}))
|
|
931
|
+
} else if (status === 'warning') {
|
|
932
|
+
console.warn(createValidatorWarningMessage({
|
|
933
|
+
columnName,
|
|
934
|
+
expectToBe,
|
|
935
|
+
from: 'Chart.addPlugin'
|
|
936
|
+
}))
|
|
937
|
+
}
|
|
938
|
+
} catch (e) {
|
|
939
|
+
// 不中斷資料流
|
|
940
|
+
console.error(createOrbChartsErrorMessage(e))
|
|
941
|
+
}
|
|
942
|
+
// add one
|
|
943
|
+
pluginsInstance$.next([...pluginsInstance$.getValue(), plugin])
|
|
944
|
+
}
|
|
945
|
+
function removePlugin (id: string) {
|
|
946
|
+
// remove one by id
|
|
947
|
+
pluginsInstance$.next(pluginsInstance$.getValue().filter(plugin => plugin._getId() !== id))
|
|
948
|
+
}
|
|
949
|
+
// function setTheme (theme: DeepPartial<Theme>) {
|
|
950
|
+
// // replace all
|
|
951
|
+
// const currentTheme = deepOverwrite(defaultTheme$.getValue(), theme)
|
|
952
|
+
// previousTheme$.next(currentTheme)
|
|
953
|
+
// currentTheme$.next(currentTheme)
|
|
954
|
+
// }
|
|
955
|
+
function updateTheme (patch: DeepPartial<Theme>) {
|
|
956
|
+
try {
|
|
957
|
+
const { status, columnName, expectToBe } = themeOptionsValidator(patch)
|
|
958
|
+
if (status === 'error') {
|
|
959
|
+
throw new Error(createValidatorErrorMessage({
|
|
960
|
+
columnName,
|
|
961
|
+
expectToBe,
|
|
962
|
+
from: 'Chart.updateTheme'
|
|
963
|
+
}))
|
|
964
|
+
} else if (status === 'warning') {
|
|
965
|
+
console.warn(createValidatorWarningMessage({
|
|
966
|
+
columnName,
|
|
967
|
+
expectToBe,
|
|
968
|
+
from: 'Chart.updateTheme'
|
|
969
|
+
}))
|
|
970
|
+
}
|
|
971
|
+
} catch (e) {
|
|
972
|
+
// 不中斷資料流
|
|
973
|
+
console.error(createOrbChartsErrorMessage(e))
|
|
974
|
+
}
|
|
975
|
+
// deep-merge with previous
|
|
976
|
+
const newTheme = deepOverwrite(currentTheme$.getValue(), patch)
|
|
977
|
+
currentTheme$.next(newTheme)
|
|
978
|
+
}
|
|
979
|
+
function forceReplaceTheme (full: Theme) {
|
|
980
|
+
// replace all
|
|
981
|
+
// previousTheme$.next(full)
|
|
982
|
+
currentTheme$.next(full)
|
|
983
|
+
}
|
|
984
|
+
function getTheme () {
|
|
985
|
+
return currentTheme$.getValue()
|
|
986
|
+
}
|
|
987
|
+
function destroy() {
|
|
988
|
+
// context.svgSelection.remove()
|
|
989
|
+
// context.canvasSelection.remove()
|
|
990
|
+
destroy$.next(undefined)
|
|
991
|
+
// 清空 element 底下所有元素(含 OrbCharts 自有的 container)
|
|
992
|
+
removeElementChildren(element)
|
|
993
|
+
orbContainer = null
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
return {
|
|
997
|
+
resize,
|
|
998
|
+
setData,
|
|
999
|
+
// setEncoding,
|
|
1000
|
+
updateEncoding,
|
|
1001
|
+
forceReplaceEncoding,
|
|
1002
|
+
getEncoding,
|
|
1003
|
+
setPlugins,
|
|
1004
|
+
addPlugin,
|
|
1005
|
+
removePlugin,
|
|
1006
|
+
// setTheme,
|
|
1007
|
+
updateTheme,
|
|
1008
|
+
forceReplaceTheme,
|
|
1009
|
+
getTheme,
|
|
1010
|
+
destroy,
|
|
1011
|
+
context
|
|
1012
|
+
}
|
|
1013
|
+
})()
|
|
1014
|
+
}
|