@orbcharts/core 3.0.0-alpha.61 → 3.0.0-alpha.63

Sign up to get free protection for your applications and to get access to all the features.
Files changed (215) hide show
  1. package/LICENSE +200 -200
  2. package/dist/lib/core-types.d.ts +1 -0
  3. package/dist/orbcharts-core.es.js +3418 -2726
  4. package/dist/orbcharts-core.umd.js +6 -2
  5. package/dist/src/AbstractChart.d.ts +5 -3
  6. package/dist/src/GridChart.d.ts +1 -1
  7. package/dist/src/MultiGridChart.d.ts +1 -1
  8. package/dist/src/MultiValueChart.d.ts +1 -1
  9. package/dist/src/RelationshipChart.d.ts +1 -1
  10. package/dist/src/SeriesChart.d.ts +1 -1
  11. package/dist/src/TreeChart.d.ts +1 -1
  12. package/dist/src/base/createBaseChart.d.ts +1 -1
  13. package/dist/src/base/createBasePlugin.d.ts +1 -1
  14. package/dist/src/base/validators/chartOptionsValidator.d.ts +3 -0
  15. package/dist/src/base/validators/chartParamsValidator.d.ts +3 -0
  16. package/dist/src/base/validators/elementValidator.d.ts +3 -0
  17. package/dist/src/base/validators/pluginsValidator.d.ts +3 -0
  18. package/dist/src/defaults.d.ts +1 -16
  19. package/dist/src/defineGridPlugin.d.ts +1 -1
  20. package/dist/src/defineMultiGridPlugin.d.ts +1 -1
  21. package/dist/src/defineMultiValuePlugin.d.ts +1 -1
  22. package/dist/src/defineNoneDataPlugin.d.ts +1 -1
  23. package/dist/src/defineRelationshipPlugin.d.ts +1 -1
  24. package/dist/src/defineSeriesPlugin.d.ts +1 -1
  25. package/dist/src/defineTreePlugin.d.ts +1 -1
  26. package/dist/src/grid/computedDataFn.d.ts +4 -0
  27. package/dist/src/grid/contextObserverCallback.d.ts +3 -0
  28. package/dist/src/grid/dataFormatterValidator.d.ts +3 -0
  29. package/dist/src/grid/dataValidator.d.ts +3 -0
  30. package/dist/src/index.d.ts +1 -1
  31. package/dist/src/multiGrid/computedDataFn.d.ts +3 -0
  32. package/dist/src/multiGrid/contextObserverCallback.d.ts +3 -0
  33. package/dist/src/multiGrid/dataFormatterValidator.d.ts +3 -0
  34. package/dist/src/multiGrid/dataValidator.d.ts +3 -0
  35. package/dist/src/multiValue/computedDataFn.d.ts +3 -0
  36. package/dist/src/multiValue/contextObserverCallback.d.ts +3 -0
  37. package/dist/src/multiValue/dataFormatterValidator.d.ts +3 -0
  38. package/dist/src/multiValue/dataValidator.d.ts +3 -0
  39. package/dist/src/relationship/computedDataFn.d.ts +3 -0
  40. package/dist/src/relationship/contextObserverCallback.d.ts +3 -0
  41. package/dist/src/relationship/dataFormatterValidator.d.ts +3 -0
  42. package/dist/src/relationship/dataValidator.d.ts +3 -0
  43. package/dist/src/series/computedDataFn.d.ts +3 -0
  44. package/dist/src/series/contextObserverCallback.d.ts +3 -0
  45. package/dist/src/series/dataFormatterValidator.d.ts +3 -0
  46. package/dist/src/series/dataValidator.d.ts +3 -0
  47. package/dist/src/tree/computedDataFn.d.ts +3 -0
  48. package/dist/src/tree/contextObserverCallback.d.ts +3 -0
  49. package/dist/src/tree/dataFormatterValidator.d.ts +3 -0
  50. package/dist/src/tree/dataValidator.d.ts +3 -0
  51. package/dist/src/utils/commonUtils.d.ts +1 -0
  52. package/dist/src/utils/errorMessage.d.ts +14 -0
  53. package/dist/src/{grid → utils}/gridObservables.d.ts +2 -2
  54. package/dist/src/utils/index.d.ts +7 -3
  55. package/dist/src/{multiGrid → utils}/multiGridObservables.d.ts +1 -1
  56. package/dist/src/utils/observables.d.ts +2 -1
  57. package/dist/src/utils/orbchartsUtils.d.ts +1 -12
  58. package/dist/src/{series → utils}/seriesObservables.d.ts +3 -3
  59. package/dist/src/{tree → utils}/treeObservables.d.ts +1 -1
  60. package/dist/src/utils/validator.d.ts +3 -0
  61. package/lib/core-types.ts +7 -0
  62. package/package.json +42 -41
  63. package/src/AbstractChart.ts +57 -48
  64. package/src/GridChart.ts +24 -20
  65. package/src/MultiGridChart.ts +24 -20
  66. package/src/MultiValueChart.ts +24 -20
  67. package/src/RelationshipChart.ts +24 -20
  68. package/src/SeriesChart.ts +24 -20
  69. package/src/TreeChart.ts +24 -20
  70. package/src/base/createBaseChart.ts +500 -388
  71. package/src/base/createBasePlugin.ts +152 -95
  72. package/src/base/validators/chartOptionsValidator.ts +24 -0
  73. package/src/base/validators/chartParamsValidator.ts +134 -0
  74. package/src/base/validators/elementValidator.ts +14 -0
  75. package/src/base/validators/pluginsValidator.ts +15 -0
  76. package/src/defaults.ts +232 -228
  77. package/src/defineGridPlugin.ts +3 -3
  78. package/src/defineMultiGridPlugin.ts +3 -3
  79. package/src/defineMultiValuePlugin.ts +3 -3
  80. package/src/defineNoneDataPlugin.ts +4 -4
  81. package/src/defineRelationshipPlugin.ts +3 -3
  82. package/src/defineSeriesPlugin.ts +3 -3
  83. package/src/defineTreePlugin.ts +3 -3
  84. package/src/grid/{computeGridData.ts → computedDataFn.ts} +129 -134
  85. package/src/grid/{createGridContextObserver.ts → contextObserverCallback.ts} +155 -155
  86. package/src/grid/dataFormatterValidator.ts +102 -0
  87. package/src/grid/dataValidator.ts +13 -0
  88. package/src/index.ts +20 -21
  89. package/src/multiGrid/{computeMultiGridData.ts → computedDataFn.ts} +123 -130
  90. package/src/multiGrid/{createMultiGridContextObserver.ts → contextObserverCallback.ts} +41 -41
  91. package/src/multiGrid/dataFormatterValidator.ts +116 -0
  92. package/src/multiGrid/dataValidator.ts +13 -0
  93. package/src/multiValue/{computeMultiValueData.ts → computedDataFn.ts} +176 -179
  94. package/src/multiValue/{createMultiValueContextObserver.ts → contextObserverCallback.ts} +12 -12
  95. package/src/multiValue/dataFormatterValidator.ts +10 -0
  96. package/src/multiValue/dataValidator.ts +10 -0
  97. package/src/relationship/{computeRelationshipData.ts → computedDataFn.ts} +125 -118
  98. package/src/relationship/{createRelationshipContextObserver.ts → contextObserverCallback.ts} +12 -12
  99. package/src/relationship/dataFormatterValidator.ts +10 -0
  100. package/src/relationship/dataValidator.ts +10 -0
  101. package/src/series/{computeSeriesData.ts → computedDataFn.ts} +88 -90
  102. package/src/series/{createSeriesContextObserver.ts → contextObserverCallback.ts} +100 -93
  103. package/src/series/dataFormatterValidator.ts +42 -0
  104. package/src/series/dataValidator.ts +13 -0
  105. package/src/tree/{computeTreeData.ts → computedDataFn.ts} +130 -132
  106. package/src/tree/{createTreeContextObserver.ts → contextObserverCallback.ts} +61 -61
  107. package/src/tree/dataFormatterValidator.ts +14 -0
  108. package/src/tree/dataValidator.ts +14 -0
  109. package/src/utils/commonUtils.ts +54 -50
  110. package/src/utils/d3Utils.ts +108 -108
  111. package/src/utils/errorMessage.ts +43 -0
  112. package/src/{grid → utils}/gridObservables.ts +611 -614
  113. package/src/utils/index.ts +10 -4
  114. package/src/{multiGrid → utils}/multiGridObservables.ts +366 -365
  115. package/src/utils/observables.ts +219 -202
  116. package/src/utils/orbchartsUtils.ts +352 -349
  117. package/src/{series → utils}/seriesObservables.ts +175 -175
  118. package/src/{tree → utils}/treeObservables.ts +94 -94
  119. package/src/utils/validator.ts +126 -0
  120. package/tsconfig.base.json +13 -13
  121. package/tsconfig.json +2 -2
  122. package/vite-env.d.ts +7 -0
  123. package/vite.config.js +22 -22
  124. package/dist/src/grid/computeGridData.d.ts +0 -6
  125. package/dist/src/grid/createGridContextObserver.d.ts +0 -3
  126. package/dist/src/multiGrid/computeMultiGridData.d.ts +0 -3
  127. package/dist/src/multiGrid/createMultiGridContextObserver.d.ts +0 -3
  128. package/dist/src/multiValue/computeMultiValueData.d.ts +0 -3
  129. package/dist/src/multiValue/createMultiValueContextObserver.d.ts +0 -3
  130. package/dist/src/relationship/computeRelationshipData.d.ts +0 -3
  131. package/dist/src/relationship/createRelationshipContextObserver.d.ts +0 -3
  132. package/dist/src/series/computeSeriesData.d.ts +0 -3
  133. package/dist/src/series/createSeriesContextObserver.d.ts +0 -3
  134. package/dist/src/tree/computeTreeData.d.ts +0 -3
  135. package/dist/src/tree/createTreeContextObserver.d.ts +0 -3
  136. package/dist/src/types/Axis.d.ts +0 -1
  137. package/dist/src/types/Chart.d.ts +0 -45
  138. package/dist/src/types/ChartParams.d.ts +0 -36
  139. package/dist/src/types/ComputedData.d.ts +0 -42
  140. package/dist/src/types/ComputedDataGrid.d.ts +0 -5
  141. package/dist/src/types/ComputedDataMultiGrid.d.ts +0 -3
  142. package/dist/src/types/ComputedDataMultiValue.d.ts +0 -6
  143. package/dist/src/types/ComputedDataRelationship.d.ts +0 -18
  144. package/dist/src/types/ComputedDataSeries.d.ts +0 -5
  145. package/dist/src/types/ComputedDataTree.d.ts +0 -7
  146. package/dist/src/types/ContextObserver.d.ts +0 -28
  147. package/dist/src/types/ContextObserverGrid.d.ts +0 -41
  148. package/dist/src/types/ContextObserverMultiGrid.d.ts +0 -15
  149. package/dist/src/types/ContextObserverMultiValue.d.ts +0 -4
  150. package/dist/src/types/ContextObserverRelationship.d.ts +0 -4
  151. package/dist/src/types/ContextObserverSeries.d.ts +0 -27
  152. package/dist/src/types/ContextObserverTree.d.ts +0 -11
  153. package/dist/src/types/ContextSubject.d.ts +0 -15
  154. package/dist/src/types/Data.d.ts +0 -19
  155. package/dist/src/types/DataFormatter.d.ts +0 -41
  156. package/dist/src/types/DataFormatterGrid.d.ts +0 -34
  157. package/dist/src/types/DataFormatterMultiGrid.d.ts +0 -20
  158. package/dist/src/types/DataFormatterMultiValue.d.ts +0 -18
  159. package/dist/src/types/DataFormatterRelationship.d.ts +0 -10
  160. package/dist/src/types/DataFormatterSeries.d.ts +0 -19
  161. package/dist/src/types/DataFormatterTree.d.ts +0 -7
  162. package/dist/src/types/DataGrid.d.ts +0 -6
  163. package/dist/src/types/DataMultiGrid.d.ts +0 -6
  164. package/dist/src/types/DataMultiValue.d.ts +0 -7
  165. package/dist/src/types/DataRelationship.d.ts +0 -21
  166. package/dist/src/types/DataSeries.d.ts +0 -6
  167. package/dist/src/types/DataTree.d.ts +0 -15
  168. package/dist/src/types/Event.d.ts +0 -56
  169. package/dist/src/types/Layout.d.ts +0 -8
  170. package/dist/src/types/Padding.d.ts +0 -6
  171. package/dist/src/types/Plugin.d.ts +0 -37
  172. package/dist/src/types/TransformData.d.ts +0 -8
  173. package/dist/src/types/index.d.ts +0 -37
  174. package/src/types/Axis.ts +0 -1
  175. package/src/types/Chart.ts +0 -54
  176. package/src/types/ChartParams.ts +0 -51
  177. package/src/types/ComputedData.ts +0 -84
  178. package/src/types/ComputedDataGrid.ts +0 -14
  179. package/src/types/ComputedDataMultiGrid.ts +0 -3
  180. package/src/types/ComputedDataMultiValue.ts +0 -9
  181. package/src/types/ComputedDataRelationship.ts +0 -20
  182. package/src/types/ComputedDataSeries.ts +0 -8
  183. package/src/types/ComputedDataTree.ts +0 -20
  184. package/src/types/ContextObserver.ts +0 -38
  185. package/src/types/ContextObserverGrid.ts +0 -43
  186. package/src/types/ContextObserverMultiGrid.ts +0 -17
  187. package/src/types/ContextObserverMultiValue.ts +0 -5
  188. package/src/types/ContextObserverRelationship.ts +0 -5
  189. package/src/types/ContextObserverSeries.ts +0 -29
  190. package/src/types/ContextObserverTree.ts +0 -11
  191. package/src/types/ContextSubject.ts +0 -18
  192. package/src/types/Data.ts +0 -45
  193. package/src/types/DataFormatter.ts +0 -74
  194. package/src/types/DataFormatterGrid.ts +0 -68
  195. package/src/types/DataFormatterMultiGrid.ts +0 -45
  196. package/src/types/DataFormatterMultiValue.ts +0 -24
  197. package/src/types/DataFormatterRelationship.ts +0 -26
  198. package/src/types/DataFormatterSeries.ts +0 -20
  199. package/src/types/DataFormatterTree.ts +0 -12
  200. package/src/types/DataGrid.ts +0 -11
  201. package/src/types/DataMultiGrid.ts +0 -7
  202. package/src/types/DataMultiValue.ts +0 -12
  203. package/src/types/DataRelationship.ts +0 -28
  204. package/src/types/DataSeries.ts +0 -11
  205. package/src/types/DataTree.ts +0 -20
  206. package/src/types/Event.ts +0 -153
  207. package/src/types/Layout.ts +0 -12
  208. package/src/types/Padding.ts +0 -6
  209. package/src/types/Plugin.ts +0 -60
  210. package/src/types/TransformData.ts +0 -8
  211. package/src/types/index.ts +0 -37
  212. /package/dist/src/{multiValue → utils}/multiValueObservables.d.ts +0 -0
  213. /package/dist/src/{relationship → utils}/relationshipObservables.d.ts +0 -0
  214. /package/src/{multiValue → utils}/multiValueObservables.ts +0 -0
  215. /package/src/{relationship → utils}/relationshipObservables.ts +0 -0
@@ -1,388 +1,500 @@
1
- import * as d3 from 'd3'
2
- import {
3
- combineLatest,
4
- iif,
5
- of,
6
- EMPTY,
7
- Subject,
8
- BehaviorSubject,
9
- Observable,
10
- first,
11
- takeUntil,
12
- catchError,
13
- throwError } from 'rxjs'
14
- import {
15
- map,
16
- mergeWith,
17
- concatMap,
18
- switchMap,
19
- switchAll,
20
- throttleTime,
21
- debounceTime,
22
- distinctUntilChanged,
23
- share,
24
- shareReplay,
25
- filter,
26
- take,
27
- startWith,
28
- scan,
29
- } from 'rxjs/operators'
30
- import type {
31
- CreateBaseChart,
32
- CreateChart,
33
- ComputedDataFn,
34
- ChartEntity,
35
- ChartType,
36
- ChartParams,
37
- ContextSubject,
38
- ComputedDataTypeMap,
39
- ContextObserverFn,
40
- ChartOptionsPartial,
41
- DataTypeMap,
42
- DataFormatterTypeMap,
43
- DataFormatterPartialTypeMap,
44
- DataFormatterBase,
45
- DataFormatterContext,
46
- Layout,
47
- PluginEntity,
48
- PluginContext,
49
- Preset,
50
- PresetPartial,
51
- ContextObserverTypeMap } from '../types'
52
- // import type { EventTypeMap } from './types/Event'
53
- import { mergeOptionsWithDefault } from '../utils'
54
- import {
55
- CHART_OPTIONS_DEFAULT,
56
- PADDING_DEFAULT,
57
- CHART_PARAMS_DEFAULT,
58
- CHART_WIDTH_DEFAULT,
59
- CHART_HEIGHT_DEFAULT } from '../defaults'
60
-
61
- // 判斷dataFormatter是否需要size參數
62
- // const isAxesTypeMap: {[key in ChartType]: Boolean} = {
63
- // series: false,
64
- // grid: true,
65
- // multiGrid: true,
66
- // multiValue: true,
67
- // tree: false,
68
- // relationship: false
69
- // }
70
-
71
- function resizeObservable(elem: HTMLElement | Element): Observable<DOMRectReadOnly> {
72
- return new Observable(subscriber => {
73
- const ro = new ResizeObserver(entries => {
74
- const entry = entries[0]
75
- if (entry && entry.contentRect) {
76
- subscriber.next(entry.contentRect)
77
- }
78
- })
79
-
80
- ro.observe(elem)
81
- return function unsubscribe() {
82
- ro.unobserve(elem)
83
- }
84
- })
85
- }
86
-
87
- function mergeDataFormatter <T>(dataFormatter: any, defaultDataFormatter: T, chartType: ChartType): T {
88
- const mergedData = mergeOptionsWithDefault(dataFormatter, defaultDataFormatter)
89
-
90
- if (chartType === 'multiGrid' && (dataFormatter as DataFormatterPartialTypeMap<'multiGrid'>).gridList != null) {
91
- // multiGrid欄位為陣列,需要各別來merge預設值
92
- (mergedData as DataFormatterTypeMap<'multiGrid'>).gridList = (dataFormatter as DataFormatterPartialTypeMap<'multiGrid'>).gridList.map((d, i) => {
93
- const defaultGrid = (defaultDataFormatter as DataFormatterTypeMap<'multiGrid'>).gridList[i] || (defaultDataFormatter as DataFormatterTypeMap<'multiGrid'>).gridList[0]
94
- return mergeOptionsWithDefault(d, defaultGrid)
95
- })
96
- }
97
- return mergedData
98
- }
99
-
100
- export const createBaseChart: CreateBaseChart = <T extends ChartType>({ defaultDataFormatter, computedDataFn, contextObserverFn }: {
101
- defaultDataFormatter: DataFormatterTypeMap<T>
102
- computedDataFn: ComputedDataFn<T>
103
- contextObserverFn: ContextObserverFn<T>
104
- }): CreateChart<T> => {
105
- const destroy$ = new Subject()
106
-
107
- const chartType: ChartType = (defaultDataFormatter as unknown as DataFormatterBase<any>).type
108
-
109
- // 建立chart實例
110
- return function createChart (element: HTMLElement | Element, options?: ChartOptionsPartial<T>): ChartEntity<T> {
111
-
112
- // -- selections --
113
- // svg selection
114
- d3.select(element).selectAll('svg').remove()
115
- const svgSelection = d3.select(element).append('svg')
116
- svgSelection
117
- .attr('xmlns:xlink', 'http://www.w3.org/1999/xlink')
118
- .attr('xmls', 'http://www.w3.org/2000/svg')
119
- .attr('version', '1.1')
120
- .style('position', 'absolute')
121
- // .style('width', '100%')
122
- // .style('height', '100%')
123
- .classed('orbcharts__root', true)
124
- // 傳入操作的 selection
125
- const selectionLayout = svgSelection.append('g')
126
- selectionLayout.classed('orbcharts__layout', true)
127
- const selectionPlugins = selectionLayout.append('g')
128
- selectionPlugins.classed('orbcharts__plugins', true)
129
-
130
- // chartSubject
131
- const chartSubject: ContextSubject<T> = {
132
- data$: new Subject(),
133
- dataFormatter$: new Subject(),
134
- plugins$: new Subject(),
135
- chartParams$: new Subject(),
136
- event$: new Subject()
137
- }
138
-
139
- // options
140
- const mergedPresetWithDefault: Preset<T, any> = ((options) => {
141
- const _options = options ? options : CHART_OPTIONS_DEFAULT as ChartOptionsPartial<T>
142
- const preset = _options.preset ? _options.preset : {} as PresetPartial<T, any>
143
-
144
- return {
145
- name: preset.name ?? '',
146
- description: preset.description ?? '',
147
- chartParams: preset.chartParams
148
- ? mergeOptionsWithDefault(preset.chartParams, CHART_PARAMS_DEFAULT)
149
- : CHART_PARAMS_DEFAULT,
150
- dataFormatter: preset.dataFormatter
151
- // ? mergeOptionsWithDefault(preset.dataFormatter, defaultDataFormatter)
152
- ? mergeDataFormatter(preset.dataFormatter, defaultDataFormatter, chartType)
153
- : defaultDataFormatter,
154
- allPluginParams: preset.allPluginParams
155
- ? preset.allPluginParams
156
- : {}
157
- }
158
- })(options)
159
-
160
- const sharedData$ = chartSubject.data$.pipe(shareReplay(1))
161
- const shareAndMergedDataFormatter$ = chartSubject.dataFormatter$
162
- .pipe(
163
- takeUntil(destroy$),
164
- startWith({}),
165
- map((dataFormatter) => {
166
- // const mergedData = mergeOptionsWithDefault(dataFormatter, mergedPresetWithDefault.dataFormatter)
167
-
168
- // if (chartType === 'multiGrid' && (dataFormatter as DataFormatterPartialTypeMap<'multiGrid'>).gridList != null) {
169
- // // multiGrid欄位為陣列,需要各別來merge預設值
170
- // (mergedData as DataFormatterTypeMap<'multiGrid'>).gridList = (dataFormatter as DataFormatterPartialTypeMap<'multiGrid'>).gridList.map(d => {
171
- // return mergeOptionsWithDefault(d, (mergedPresetWithDefault.dataFormatter as DataFormatterTypeMap<'multiGrid'>).gridList[0])
172
- // })
173
- // }
174
- // return mergedData
175
- return mergeDataFormatter(dataFormatter, mergedPresetWithDefault.dataFormatter, chartType)
176
- }),
177
- shareReplay(1)
178
- )
179
- const shareAndMergedChartParams$ = chartSubject.chartParams$
180
- .pipe(
181
- takeUntil(destroy$),
182
- startWith({}),
183
- map((d) => {
184
- return mergeOptionsWithDefault(d, mergedPresetWithDefault.chartParams)
185
- }),
186
- shareReplay(1)
187
- )
188
-
189
- // -- size --
190
- // padding
191
- const mergedPadding$ = shareAndMergedChartParams$
192
- .pipe(
193
- takeUntil(destroy$),
194
- startWith({}),
195
- map((d: any) => {
196
- return mergeOptionsWithDefault(d.padding ?? {}, PADDING_DEFAULT)
197
- })
198
- )
199
- mergedPadding$
200
- .pipe(
201
- takeUntil(destroy$),
202
- first()
203
- )
204
- .subscribe(d => {
205
- selectionLayout
206
- .attr('transform', `translate(${d.left}, ${d.top})`)
207
- })
208
- mergedPadding$.subscribe(size => {
209
- selectionLayout
210
- .transition()
211
- .attr('transform', `translate(${size.left}, ${size.top})`)
212
- })
213
-
214
- // 監聽外層的element尺寸
215
- const rootSize$: Observable<{ width: number; height: number }> = of({
216
- width: options?.width ?? CHART_OPTIONS_DEFAULT.width,
217
- height: options?.height ?? CHART_OPTIONS_DEFAULT.height
218
- }).pipe(
219
- switchMap(size => {
220
- // console.log('options', options)
221
- // console.log('size', size)
222
- return iif(
223
- () => size.width === 'auto' || size.height === 'auto',
224
- // 'auto' 的話就監聽element的尺寸
225
- resizeObservable(element).pipe(
226
- map((d) => {
227
- return {
228
- width: size.width === 'auto' ? d.width : size.width,
229
- height: size.height === 'auto' ? d.height : size.height
230
- }
231
- })
232
- ),
233
- of(size as { width: number; height: number })
234
- )
235
- }),
236
- takeUntil(destroy$),
237
- share()
238
- )
239
- const rootSizeFiltered$ = of().pipe(
240
- mergeWith(
241
- rootSize$.pipe(
242
- debounceTime(250)
243
- ),
244
- rootSize$.pipe(
245
- throttleTime(250)
246
- )
247
- ),
248
- distinctUntilChanged((a, b) => JSON.stringify(a) === JSON.stringify(b)),
249
- share()
250
- )
251
- const rootSizeSubscription = rootSizeFiltered$.subscribe()
252
-
253
- // layout
254
- const layout$: Observable<Layout> = combineLatest({
255
- rootSize: rootSizeFiltered$,
256
- mergedPadding: mergedPadding$
257
- }).pipe(
258
- takeUntil(destroy$),
259
- switchMap(async (d) => {
260
- const rootWidth = d.rootSize.width > 0
261
- ? d.rootSize.width
262
- : CHART_WIDTH_DEFAULT
263
- const rootHeight = d.rootSize.height > 0
264
- ? d.rootSize.height
265
- : CHART_HEIGHT_DEFAULT
266
- return {
267
- width: rootWidth - d.mergedPadding.left - d.mergedPadding.right,
268
- height: rootHeight - d.mergedPadding.top - d.mergedPadding.bottom,
269
- top: d.mergedPadding.top,
270
- right: d.mergedPadding.right,
271
- bottom: d.mergedPadding.bottom,
272
- left: d.mergedPadding.left,
273
- rootWidth,
274
- rootHeight
275
- }
276
- }),
277
- shareReplay(1)
278
- )
279
- layout$.subscribe(d => {
280
- svgSelection
281
- .attr('width', d.rootWidth)
282
- .attr('height', d.rootHeight)
283
- })
284
-
285
- // -- computedData --
286
- const computedData$: Observable<ComputedDataTypeMap<T>> = combineLatest({
287
- data: sharedData$,
288
- dataFormatter: shareAndMergedDataFormatter$,
289
- chartParams: shareAndMergedChartParams$,
290
- // layout: iif(() => isAxesTypeMap[chartType] === true, layout$, of(undefined))
291
- }).pipe(
292
- takeUntil(destroy$),
293
- switchMap(async d => d),
294
- switchMap((d) => {
295
- return of(d)
296
- .pipe(
297
- map(_d => {
298
- try {
299
- return computedDataFn({ data: _d.data, dataFormatter: _d.dataFormatter, chartParams: _d.chartParams })
300
- } catch (e) {
301
- console.error(e)
302
- throw new Error(e)
303
- }
304
- }),
305
- catchError(() => EMPTY)
306
- )
307
- }),
308
- shareReplay(1)
309
- )
310
-
311
- // subscribe - computedData組合了所有的chart參數,所以訂閱computedData可以一次訂閱所有的資料流
312
- computedData$.subscribe()
313
-
314
- // -- plugins --
315
- const pluginEntityMap: any = {} // 用於destroy
316
- chartSubject.plugins$.subscribe(plugins => {
317
- if (!plugins) {
318
- return
319
- }
320
- // 建立<g>
321
- const update = selectionPlugins
322
- .selectAll<SVGGElement, PluginEntity<T, any, any>>('g.orbcharts__plugin')
323
- .data(plugins, d => d.name as string)
324
- const enter = update.enter()
325
- .append('g')
326
- .attr('class', plugin => {
327
- return `orbcharts__plugin orbcharts__${plugin.name}`
328
- })
329
- const exit = update.exit()
330
- .remove()
331
-
332
- // destroy entity
333
- exit.each((plugin: PluginEntity<T, unknown, unknown>, i, n) => {
334
- if (pluginEntityMap[plugin.name as string]) {
335
- pluginEntityMap[plugin.name as string].destroy()
336
- pluginEntityMap[plugin.name as string] = undefined
337
- }
338
- })
339
-
340
- enter.each((plugin, i, n) => {
341
- const _pluginObserverBase = {
342
- fullParams$: new Observable(),
343
- fullChartParams$: shareAndMergedChartParams$,
344
- fullDataFormatter$: shareAndMergedDataFormatter$,
345
- computedData$,
346
- layout$
347
- }
348
- const pluginObserver: ContextObserverTypeMap<T, typeof plugin.defaultParams> = contextObserverFn({
349
- observer: _pluginObserverBase,
350
- subject: chartSubject
351
- })
352
-
353
- // -- createPlugin(plugin) --
354
- const pluginSelection = d3.select(n[i])
355
- const pluginContext: PluginContext<T, typeof plugin.name, typeof plugin.defaultParams> = {
356
- selection: pluginSelection,
357
- rootSelection: svgSelection,
358
- name: plugin.name,
359
- chartType,
360
- subject: chartSubject,
361
- observer: pluginObserver
362
- }
363
-
364
- plugin.setPresetParams(mergedPresetWithDefault.allPluginParams[plugin.name] ?? {})
365
- // 傳入context
366
- plugin.setContext(pluginContext)
367
-
368
- // 紀錄起來
369
- pluginEntityMap[pluginContext.name as string] = plugin
370
-
371
- // init plugin
372
- plugin.init()
373
-
374
- })
375
-
376
- })
377
-
378
- return {
379
- ...chartSubject,
380
- selection: svgSelection,
381
- destroy () {
382
- d3.select(element).selectAll('svg').remove()
383
- destroy$.next(undefined)
384
- rootSizeSubscription.unsubscribe()
385
- }
386
- }
387
- }
388
- }
1
+ import * as d3 from 'd3'
2
+ import {
3
+ combineLatest,
4
+ iif,
5
+ of,
6
+ EMPTY,
7
+ Subject,
8
+ BehaviorSubject,
9
+ Observable,
10
+ first,
11
+ takeUntil,
12
+ catchError,
13
+ throwError } from 'rxjs'
14
+ import {
15
+ map,
16
+ mergeWith,
17
+ concatMap,
18
+ switchMap,
19
+ switchAll,
20
+ throttleTime,
21
+ debounceTime,
22
+ distinctUntilChanged,
23
+ share,
24
+ shareReplay,
25
+ filter,
26
+ take,
27
+ startWith,
28
+ scan,
29
+ } from 'rxjs/operators'
30
+ import type {
31
+ CreateBaseChart,
32
+ CreateChart,
33
+ ComputedDataFn,
34
+ ChartEntity,
35
+ ChartType,
36
+ ChartParams,
37
+ ChartParamsPartial,
38
+ ContextSubject,
39
+ ComputedDataTypeMap,
40
+ ContextObserverCallback,
41
+ ChartOptionsPartial,
42
+ DataTypeMap,
43
+ DataFormatterTypeMap,
44
+ DataFormatterPartialTypeMap,
45
+ DataFormatterBase,
46
+ DataFormatterContext,
47
+ DataFormatterValidator,
48
+ DataValidator,
49
+ Layout,
50
+ PluginEntity,
51
+ PluginContext,
52
+ Preset,
53
+ PresetPartial,
54
+ ContextObserverTypeMap,
55
+ ValidatorResult,
56
+ } from '../../lib/core-types'
57
+ import { mergeOptionsWithDefault, resizeObservable } from '../utils'
58
+ import { createValidatorErrorMessage, createValidatorWarningMessage, createOrbChartsErrorMessage } from '../utils/errorMessage'
59
+ import { chartOptionsValidator } from './validators/chartOptionsValidator'
60
+ import { elementValidator } from './validators/elementValidator'
61
+ import { chartParamsValidator } from './validators/chartParamsValidator'
62
+ import { pluginsValidator } from './validators/pluginsValidator'
63
+ import {
64
+ CHART_OPTIONS_DEFAULT,
65
+ PADDING_DEFAULT,
66
+ CHART_PARAMS_DEFAULT,
67
+ CHART_WIDTH_DEFAULT,
68
+ CHART_HEIGHT_DEFAULT } from '../defaults'
69
+
70
+ // 判斷dataFormatter是否需要size參數
71
+ // const isAxesTypeMap: {[key in ChartType]: Boolean} = {
72
+ // series: false,
73
+ // grid: true,
74
+ // multiGrid: true,
75
+ // multiValue: true,
76
+ // tree: false,
77
+ // relationship: false
78
+ // }
79
+
80
+
81
+ function mergeDataFormatter <T>(dataFormatter: any, defaultDataFormatter: T, chartType: ChartType): T {
82
+ const mergedData = mergeOptionsWithDefault(dataFormatter, defaultDataFormatter)
83
+
84
+ if (chartType === 'multiGrid' && (dataFormatter as DataFormatterPartialTypeMap<'multiGrid'>).gridList != null) {
85
+ // multiGrid欄位為陣列,需要各別來merge預設值
86
+ (mergedData as DataFormatterTypeMap<'multiGrid'>).gridList = (dataFormatter as DataFormatterPartialTypeMap<'multiGrid'>).gridList.map((d, i) => {
87
+ const defaultGrid = (defaultDataFormatter as DataFormatterTypeMap<'multiGrid'>).gridList[i] || (defaultDataFormatter as DataFormatterTypeMap<'multiGrid'>).gridList[0]
88
+ return mergeOptionsWithDefault(d, defaultGrid)
89
+ })
90
+ }
91
+ return mergedData
92
+ }
93
+
94
+ export const createBaseChart: CreateBaseChart = <T extends ChartType>({
95
+ defaultDataFormatter,
96
+ dataFormatterValidator,
97
+ computedDataFn,
98
+ dataValidator,
99
+ contextObserverCallback
100
+ }: {
101
+ defaultDataFormatter: DataFormatterTypeMap<T>
102
+ dataFormatterValidator: DataFormatterValidator<T>
103
+ computedDataFn: ComputedDataFn<T>
104
+ dataValidator: DataValidator<T>
105
+ contextObserverCallback: ContextObserverCallback<T>
106
+ }): CreateChart<T> => {
107
+ const destroy$ = new Subject()
108
+
109
+ const chartType: ChartType = (defaultDataFormatter as unknown as DataFormatterBase<any>).type
110
+
111
+ // 建立chart實例
112
+ return function createChart (element: HTMLElement | Element, options?: ChartOptionsPartial<T>): ChartEntity<T> {
113
+ try {
114
+ const { status, columnName, expectToBe } = chartOptionsValidator(options)
115
+ if (status === 'error') {
116
+ throw new Error(createValidatorErrorMessage({
117
+ columnName,
118
+ expectToBe,
119
+ from: 'Chart.constructor'
120
+ }))
121
+ } else if (status === 'warning') {
122
+ console.warn(createValidatorWarningMessage({
123
+ columnName,
124
+ expectToBe,
125
+ from: 'Chart.constructor'
126
+ }))
127
+ } else {
128
+ const { status, columnName, expectToBe } = elementValidator(element)
129
+ if (status === 'error') {
130
+ throw new Error(createValidatorErrorMessage({
131
+ columnName,
132
+ expectToBe,
133
+ from: 'Chart.constructor'
134
+ }))
135
+ } else if (status === 'warning') {
136
+ console.warn(createValidatorWarningMessage({
137
+ columnName,
138
+ expectToBe,
139
+ from: 'Chart.constructor'
140
+ }))
141
+ }
142
+ }
143
+ } catch (e) {
144
+ throw new Error(e)
145
+ }
146
+
147
+
148
+ // -- selections --
149
+ // svg selection
150
+ d3.select(element).selectAll('svg').remove()
151
+ const svgSelection = d3.select(element).append('svg')
152
+ svgSelection
153
+ .attr('xmlns:xlink', 'http://www.w3.org/1999/xlink')
154
+ .attr('xmls', 'http://www.w3.org/2000/svg')
155
+ .attr('version', '1.1')
156
+ .style('position', 'absolute')
157
+ // .style('width', '100%')
158
+ // .style('height', '100%')
159
+ .classed('orbcharts__root', true)
160
+ // 傳入操作的 selection
161
+ const selectionLayout = svgSelection.append('g')
162
+ selectionLayout.classed('orbcharts__layout', true)
163
+ const selectionPlugins = selectionLayout.append('g')
164
+ selectionPlugins.classed('orbcharts__plugins', true)
165
+
166
+ // chartSubject
167
+ const chartSubject: ContextSubject<T> = {
168
+ data$: new Subject(),
169
+ dataFormatter$: new Subject(),
170
+ plugins$: new Subject(),
171
+ chartParams$: new Subject(),
172
+ event$: new Subject()
173
+ }
174
+
175
+ // options
176
+ const mergedPresetWithDefault: Preset<T, any> = ((options) => {
177
+ const _options = options ? options : CHART_OPTIONS_DEFAULT as ChartOptionsPartial<T>
178
+ const preset = _options.preset ? _options.preset : {} as PresetPartial<T, any>
179
+
180
+ return {
181
+ name: preset.name ?? '',
182
+ description: preset.description ?? '',
183
+ chartParams: preset.chartParams
184
+ ? mergeOptionsWithDefault(preset.chartParams, CHART_PARAMS_DEFAULT)
185
+ : CHART_PARAMS_DEFAULT,
186
+ dataFormatter: preset.dataFormatter
187
+ // ? mergeOptionsWithDefault(preset.dataFormatter, defaultDataFormatter)
188
+ ? mergeDataFormatter(preset.dataFormatter, defaultDataFormatter, chartType)
189
+ : defaultDataFormatter,
190
+ allPluginParams: preset.allPluginParams
191
+ ? preset.allPluginParams
192
+ : {}
193
+ }
194
+ })(options)
195
+
196
+ const sharedData$ = chartSubject.data$.pipe(shareReplay(1))
197
+ const shareAndMergedDataFormatter$ = chartSubject.dataFormatter$
198
+ .pipe(
199
+ takeUntil(destroy$),
200
+ startWith({} as DataFormatterPartialTypeMap<T>),
201
+ map((dataFormatter) => {
202
+ try {
203
+ // 檢查 dataFormatter$ 資料格式是否正確
204
+ const { status, columnName, expectToBe } = dataFormatterValidator(dataFormatter)
205
+ if (status === 'error') {
206
+ throw new Error(createValidatorErrorMessage({
207
+ columnName,
208
+ expectToBe,
209
+ from: 'Chart.constructor'
210
+ }))
211
+ } else if (status === 'warning') {
212
+ console.warn(createValidatorWarningMessage({
213
+ columnName,
214
+ expectToBe,
215
+ from: 'Chart.constructor'
216
+ }))
217
+ }
218
+
219
+ return mergeDataFormatter(dataFormatter, mergedPresetWithDefault.dataFormatter, chartType)
220
+ } catch (e) {
221
+ throw new Error(e)
222
+ }
223
+ }),
224
+ catchError((e) => {
225
+ console.error(createOrbChartsErrorMessage(e))
226
+ return EMPTY
227
+ }),
228
+ shareReplay(1)
229
+ )
230
+ const shareAndMergedChartParams$ = chartSubject.chartParams$
231
+ .pipe(
232
+ takeUntil(destroy$),
233
+ startWith({}),
234
+ map((d) => {
235
+ try {
236
+ // 檢查 chartParams$ 資料格式是否正確
237
+ const { status, columnName, expectToBe } = chartParamsValidator(chartType, d)
238
+ if (status === 'error') {
239
+ throw new Error(createValidatorErrorMessage({
240
+ columnName,
241
+ expectToBe,
242
+ from: 'Chart.constructor'
243
+ }))
244
+ } else if (status === 'warning') {
245
+ console.warn(createValidatorWarningMessage({
246
+ columnName,
247
+ expectToBe,
248
+ from: 'Chart.constructor'
249
+ }))
250
+ }
251
+
252
+ return mergeOptionsWithDefault(d, mergedPresetWithDefault.chartParams)
253
+ } catch (e) {
254
+ throw new Error(e)
255
+ }
256
+ }),
257
+ catchError((e) => {
258
+ console.error(createOrbChartsErrorMessage(e))
259
+ return EMPTY
260
+ }),
261
+ shareReplay(1)
262
+ )
263
+
264
+ // -- size --
265
+ // padding
266
+ const mergedPadding$ = shareAndMergedChartParams$
267
+ .pipe(
268
+ takeUntil(destroy$),
269
+ startWith({}),
270
+ map((d: any) => {
271
+ return mergeOptionsWithDefault(d.padding ?? {}, PADDING_DEFAULT)
272
+ })
273
+ )
274
+ mergedPadding$
275
+ .pipe(
276
+ takeUntil(destroy$),
277
+ first()
278
+ )
279
+ .subscribe(d => {
280
+ selectionLayout
281
+ .attr('transform', `translate(${d.left}, ${d.top})`)
282
+ })
283
+ mergedPadding$.subscribe(size => {
284
+ selectionLayout
285
+ .transition()
286
+ .attr('transform', `translate(${size.left}, ${size.top})`)
287
+ })
288
+
289
+ // 監聽外層的element尺寸
290
+ const rootSize$: Observable<{ width: number; height: number }> = of({
291
+ width: options?.width ?? CHART_OPTIONS_DEFAULT.width,
292
+ height: options?.height ?? CHART_OPTIONS_DEFAULT.height
293
+ }).pipe(
294
+ switchMap(size => {
295
+ return iif(
296
+ () => size.width === 'auto' || size.height === 'auto',
297
+ // 'auto' 的話就監聽element的尺寸
298
+ resizeObservable(element).pipe(
299
+ map((d) => {
300
+ return {
301
+ width: size.width === 'auto' ? d.width : size.width,
302
+ height: size.height === 'auto' ? d.height : size.height
303
+ }
304
+ })
305
+ ),
306
+ of(size as { width: number; height: number })
307
+ )
308
+ }),
309
+ takeUntil(destroy$),
310
+ share()
311
+ )
312
+ const rootSizeFiltered$ = of().pipe(
313
+ mergeWith(
314
+ rootSize$.pipe(
315
+ debounceTime(250)
316
+ ),
317
+ rootSize$.pipe(
318
+ throttleTime(250)
319
+ )
320
+ ),
321
+ distinctUntilChanged((a, b) => JSON.stringify(a) === JSON.stringify(b)),
322
+ share()
323
+ )
324
+ const rootSizeSubscription = rootSizeFiltered$.subscribe()
325
+
326
+ // layout
327
+ const layout$: Observable<Layout> = combineLatest({
328
+ rootSize: rootSizeFiltered$,
329
+ mergedPadding: mergedPadding$
330
+ }).pipe(
331
+ takeUntil(destroy$),
332
+ switchMap(async (d) => {
333
+ const rootWidth = d.rootSize.width > 0
334
+ ? d.rootSize.width
335
+ : CHART_WIDTH_DEFAULT
336
+ const rootHeight = d.rootSize.height > 0
337
+ ? d.rootSize.height
338
+ : CHART_HEIGHT_DEFAULT
339
+ return {
340
+ width: rootWidth - d.mergedPadding.left - d.mergedPadding.right,
341
+ height: rootHeight - d.mergedPadding.top - d.mergedPadding.bottom,
342
+ top: d.mergedPadding.top,
343
+ right: d.mergedPadding.right,
344
+ bottom: d.mergedPadding.bottom,
345
+ left: d.mergedPadding.left,
346
+ rootWidth,
347
+ rootHeight
348
+ }
349
+ }),
350
+ shareReplay(1)
351
+ )
352
+ layout$.subscribe(d => {
353
+ svgSelection
354
+ .attr('width', d.rootWidth)
355
+ .attr('height', d.rootHeight)
356
+ })
357
+
358
+ // -- computedData --
359
+ const computedData$: Observable<ComputedDataTypeMap<T>> = combineLatest({
360
+ data: sharedData$,
361
+ dataFormatter: shareAndMergedDataFormatter$,
362
+ chartParams: shareAndMergedChartParams$,
363
+ // layout: iif(() => isAxesTypeMap[chartType] === true, layout$, of(undefined))
364
+ }).pipe(
365
+ takeUntil(destroy$),
366
+ switchMap(async d => d),
367
+ switchMap((d) => {
368
+ return of(d)
369
+ .pipe(
370
+ map(_d => {
371
+ try {
372
+ // 檢查 data$ 資料格式是否正確
373
+ const { status, columnName, expectToBe } = dataValidator(_d.data)
374
+ if (status === 'error') {
375
+ throw new Error(createValidatorErrorMessage({
376
+ columnName,
377
+ expectToBe,
378
+ from: 'Chart.constructor'
379
+ }))
380
+ } else if (status === 'warning') {
381
+ console.warn(createValidatorWarningMessage({
382
+ columnName,
383
+ expectToBe,
384
+ from: 'Chart.constructor'
385
+ }))
386
+ }
387
+
388
+ return computedDataFn({ data: _d.data, dataFormatter: _d.dataFormatter, chartParams: _d.chartParams })
389
+ } catch (e) {
390
+ throw new Error(e)
391
+ }
392
+ }),
393
+ catchError((e) => {
394
+ console.error(createOrbChartsErrorMessage(e))
395
+ return EMPTY
396
+ })
397
+ )
398
+ }),
399
+ shareReplay(1)
400
+ )
401
+
402
+ // subscribe - computedData組合了所有的chart參數,所以訂閱computedData可以一次訂閱所有的資料流
403
+ computedData$.subscribe()
404
+
405
+ // -- plugins --
406
+ const pluginEntityMap: any = {} // 用於destroy
407
+ chartSubject.plugins$.subscribe(plugins => {
408
+ try {
409
+ // 檢查 plugins$ 資料格式是否正確
410
+ const { status, columnName, expectToBe } = pluginsValidator(chartType, plugins)
411
+ if (status === 'error') {
412
+ throw new Error(createValidatorErrorMessage({
413
+ columnName,
414
+ expectToBe,
415
+ from: 'Chart.plugins$'
416
+ }))
417
+ } else if (status === 'warning') {
418
+ console.warn(createValidatorWarningMessage({
419
+ columnName,
420
+ expectToBe,
421
+ from: 'Chart.plugins$'
422
+ }))
423
+ }
424
+ } catch (e) {
425
+ throw new Error(e)
426
+ }
427
+
428
+ selectionPlugins
429
+ .selectAll<SVGGElement, PluginEntity<T, any, any>>('g.orbcharts__plugin')
430
+ .data(plugins, d => d.name as string)
431
+ .join(
432
+ enter => {
433
+ return enter
434
+ .append('g')
435
+ .attr('class', plugin => {
436
+ return `orbcharts__plugin orbcharts__${plugin.name}`
437
+ })
438
+ .each((plugin, i, n) => {
439
+ const _pluginObserverBase = {
440
+ fullParams$: new Observable(),
441
+ fullChartParams$: shareAndMergedChartParams$,
442
+ fullDataFormatter$: shareAndMergedDataFormatter$,
443
+ computedData$,
444
+ layout$
445
+ }
446
+ const pluginObserver: ContextObserverTypeMap<T, typeof plugin.defaultParams> = contextObserverCallback({
447
+ observer: _pluginObserverBase,
448
+ subject: chartSubject
449
+ })
450
+
451
+ // -- createPlugin(plugin) --
452
+ const pluginSelection = d3.select(n[i])
453
+ const pluginContext: PluginContext<T, typeof plugin.name, typeof plugin.defaultParams> = {
454
+ selection: pluginSelection,
455
+ rootSelection: svgSelection,
456
+ name: plugin.name,
457
+ chartType,
458
+ subject: chartSubject,
459
+ observer: pluginObserver
460
+ }
461
+
462
+ plugin.setPresetParams(mergedPresetWithDefault.allPluginParams[plugin.name] ?? {})
463
+ // 傳入context
464
+ plugin.setContext(pluginContext)
465
+
466
+ // 紀錄起來
467
+ pluginEntityMap[pluginContext.name as string] = plugin
468
+
469
+ // init plugin
470
+ plugin.init()
471
+
472
+ })
473
+ },
474
+ update => update,
475
+ exit => {
476
+ return exit
477
+ .each((plugin: PluginEntity<T, unknown, unknown>, i, n) => {
478
+ if (pluginEntityMap[plugin.name as string]) {
479
+ pluginEntityMap[plugin.name as string].destroy()
480
+ pluginEntityMap[plugin.name as string] = undefined
481
+ }
482
+ })
483
+ .remove()
484
+ }
485
+ )
486
+ .sort((a, b) => a.layerIndex - b.layerIndex)
487
+
488
+ })
489
+
490
+ return {
491
+ ...chartSubject,
492
+ selection: svgSelection,
493
+ destroy () {
494
+ d3.select(element).selectAll('svg').remove()
495
+ destroy$.next(undefined)
496
+ rootSizeSubscription.unsubscribe()
497
+ }
498
+ }
499
+ }
500
+ }