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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (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 +3054 -2609
  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 +9 -0
  87. package/src/grid/dataValidator.ts +9 -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 +9 -0
  92. package/src/multiGrid/dataValidator.ts +9 -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 +9 -0
  96. package/src/multiValue/dataValidator.ts +9 -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 +9 -0
  100. package/src/relationship/dataValidator.ts +9 -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 +9 -0
  104. package/src/series/dataValidator.ts +9 -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 +9 -0
  108. package/src/tree/dataValidator.ts +9 -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 +218 -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
+ }