@orbcharts/core 3.0.0-alpha.43 → 3.0.0-alpha.45

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 (85) hide show
  1. package/LICENSE +200 -200
  2. package/dist/orbcharts-core.es.js +97 -97
  3. package/dist/orbcharts-core.umd.js +2 -2
  4. package/dist/src/series/seriesObservables.d.ts +3 -3
  5. package/dist/src/types/ContextObserverSeries.d.ts +1 -1
  6. package/dist/src/types/DataFormatterSeries.d.ts +3 -3
  7. package/package.json +41 -41
  8. package/src/AbstractChart.ts +48 -48
  9. package/src/GridChart.ts +20 -20
  10. package/src/MultiGridChart.ts +20 -20
  11. package/src/MultiValueChart.ts +20 -20
  12. package/src/RelationshipChart.ts +20 -20
  13. package/src/SeriesChart.ts +20 -20
  14. package/src/TreeChart.ts +20 -20
  15. package/src/base/createBaseChart.ts +368 -367
  16. package/src/base/createBasePlugin.ts +89 -89
  17. package/src/defaults.ts +220 -220
  18. package/src/defineGridPlugin.ts +3 -3
  19. package/src/defineMultiGridPlugin.ts +3 -3
  20. package/src/defineMultiValuePlugin.ts +3 -3
  21. package/src/defineNoneDataPlugin.ts +4 -4
  22. package/src/defineRelationshipPlugin.ts +3 -3
  23. package/src/defineSeriesPlugin.ts +3 -3
  24. package/src/defineTreePlugin.ts +3 -3
  25. package/src/grid/computeGridData.ts +134 -134
  26. package/src/grid/createGridContextObserver.ts +147 -147
  27. package/src/grid/gridObservables.ts +573 -573
  28. package/src/index.ts +21 -21
  29. package/src/multiGrid/computeMultiGridData.ts +130 -130
  30. package/src/multiGrid/createMultiGridContextObserver.ts +40 -40
  31. package/src/multiGrid/multiGridObservables.ts +350 -350
  32. package/src/multiValue/computeMultiValueData.ts +143 -143
  33. package/src/multiValue/createMultiValueContextObserver.ts +12 -12
  34. package/src/relationship/computeRelationshipData.ts +118 -118
  35. package/src/relationship/createRelationshipContextObserver.ts +12 -12
  36. package/src/series/computeSeriesData.ts +90 -90
  37. package/src/series/createSeriesContextObserver.ts +93 -93
  38. package/src/series/seriesObservables.ts +175 -175
  39. package/src/tree/computeTreeData.ts +131 -131
  40. package/src/tree/createTreeContextObserver.ts +61 -61
  41. package/src/tree/treeObservables.ts +94 -94
  42. package/src/types/Chart.ts +48 -48
  43. package/src/types/ChartParams.ts +51 -51
  44. package/src/types/ComputedData.ts +83 -83
  45. package/src/types/ComputedDataGrid.ts +13 -13
  46. package/src/types/ComputedDataMultiGrid.ts +2 -2
  47. package/src/types/ComputedDataMultiValue.ts +9 -9
  48. package/src/types/ComputedDataRelationship.ts +19 -19
  49. package/src/types/ComputedDataSeries.ts +7 -7
  50. package/src/types/ComputedDataTree.ts +19 -19
  51. package/src/types/ContextObserver.ts +38 -38
  52. package/src/types/ContextObserverGrid.ts +41 -41
  53. package/src/types/ContextObserverMultiGrid.ts +15 -15
  54. package/src/types/ContextObserverMultiValue.ts +4 -4
  55. package/src/types/ContextObserverRelationship.ts +4 -4
  56. package/src/types/ContextObserverSeries.ts +29 -29
  57. package/src/types/ContextObserverTree.ts +11 -11
  58. package/src/types/ContextSubject.ts +18 -18
  59. package/src/types/Data.ts +45 -45
  60. package/src/types/DataFormatter.ts +74 -74
  61. package/src/types/DataFormatterGrid.ts +67 -67
  62. package/src/types/DataFormatterMultiGrid.ts +44 -44
  63. package/src/types/DataFormatterMultiValue.ts +23 -23
  64. package/src/types/DataFormatterRelationship.ts +25 -25
  65. package/src/types/DataFormatterSeries.ts +20 -20
  66. package/src/types/DataFormatterTree.ts +12 -12
  67. package/src/types/DataGrid.ts +11 -11
  68. package/src/types/DataMultiGrid.ts +6 -6
  69. package/src/types/DataMultiValue.ts +12 -12
  70. package/src/types/DataRelationship.ts +27 -27
  71. package/src/types/DataSeries.ts +11 -11
  72. package/src/types/DataTree.ts +20 -20
  73. package/src/types/Event.ts +153 -153
  74. package/src/types/Layout.ts +11 -11
  75. package/src/types/Padding.ts +5 -5
  76. package/src/types/Plugin.ts +60 -60
  77. package/src/types/TransformData.ts +7 -7
  78. package/src/types/index.ts +37 -37
  79. package/src/utils/commonUtils.ts +50 -50
  80. package/src/utils/d3Utils.ts +89 -89
  81. package/src/utils/index.ts +4 -4
  82. package/src/utils/observables.ts +201 -201
  83. package/src/utils/orbchartsUtils.ts +349 -349
  84. package/tsconfig.json +13 -13
  85. package/vite.config.js +44 -44
@@ -1,367 +1,368 @@
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> = ((options) => {
141
- const _options = options ? options : CHART_OPTIONS_DEFAULT as ChartOptionsPartial<T>
142
- const preset = _options.preset ? _options.preset : {} as PresetPartial<T>
143
-
144
- return {
145
- chartParams: preset.chartParams
146
- ? mergeOptionsWithDefault(preset.chartParams, CHART_PARAMS_DEFAULT)
147
- : CHART_PARAMS_DEFAULT,
148
- dataFormatter: preset.dataFormatter
149
- // ? mergeOptionsWithDefault(preset.dataFormatter, defaultDataFormatter)
150
- ? mergeDataFormatter(preset.dataFormatter, defaultDataFormatter, chartType)
151
- : defaultDataFormatter,
152
- allPluginParams: preset.allPluginParams
153
- ? preset.allPluginParams
154
- : {},
155
- description: preset.description ?? ''
156
- }
157
- })(options)
158
-
159
- const sharedData$ = chartSubject.data$.pipe(shareReplay(1))
160
- const shareAndMergedDataFormatter$ = chartSubject.dataFormatter$
161
- .pipe(
162
- takeUntil(destroy$),
163
- startWith({}),
164
- map((dataFormatter) => {
165
- // const mergedData = mergeOptionsWithDefault(dataFormatter, mergedPresetWithDefault.dataFormatter)
166
-
167
- // if (chartType === 'multiGrid' && (dataFormatter as DataFormatterPartialTypeMap<'multiGrid'>).gridList != null) {
168
- // // multiGrid欄位為陣列,需要各別來merge預設值
169
- // (mergedData as DataFormatterTypeMap<'multiGrid'>).gridList = (dataFormatter as DataFormatterPartialTypeMap<'multiGrid'>).gridList.map(d => {
170
- // return mergeOptionsWithDefault(d, (mergedPresetWithDefault.dataFormatter as DataFormatterTypeMap<'multiGrid'>).gridList[0])
171
- // })
172
- // }
173
- // return mergedData
174
- return mergeDataFormatter(dataFormatter, mergedPresetWithDefault.dataFormatter, chartType)
175
- }),
176
- shareReplay(1)
177
- )
178
- const shareAndMergedChartParams$ = chartSubject.chartParams$
179
- .pipe(
180
- takeUntil(destroy$),
181
- startWith({}),
182
- map((d) => {
183
- return mergeOptionsWithDefault(d, mergedPresetWithDefault.chartParams)
184
- }),
185
- shareReplay(1)
186
- )
187
-
188
- // -- size --
189
- // padding
190
- const mergedPadding$ = shareAndMergedChartParams$
191
- .pipe(
192
- takeUntil(destroy$),
193
- startWith({}),
194
- map((d: any) => {
195
- return mergeOptionsWithDefault(d.padding ?? {}, PADDING_DEFAULT)
196
- })
197
- )
198
- mergedPadding$
199
- .pipe(
200
- takeUntil(destroy$),
201
- first()
202
- )
203
- .subscribe(d => {
204
- selectionLayout
205
- .attr('transform', `translate(${d.left}, ${d.top})`)
206
- })
207
- mergedPadding$.subscribe(size => {
208
- selectionLayout
209
- .transition()
210
- .attr('transform', `translate(${size.left}, ${size.top})`)
211
- })
212
-
213
- // 監聽外層的element尺寸
214
- const rootSize$ = resizeObservable(element)
215
- .pipe(
216
- takeUntil(destroy$),
217
- share()
218
- )
219
- const rootSizeFiltered$ = of().pipe(
220
- mergeWith(
221
- rootSize$.pipe(
222
- debounceTime(250)
223
- ),
224
- rootSize$.pipe(
225
- throttleTime(250)
226
- )
227
- ),
228
- distinctUntilChanged((a, b) => JSON.stringify(a) === JSON.stringify(b)),
229
- share()
230
- )
231
- const rootSizeSubscription = rootSizeFiltered$.subscribe()
232
-
233
- // layout
234
- const layout$: Observable<Layout> = combineLatest({
235
- rootSize: rootSizeFiltered$,
236
- mergedPadding: mergedPadding$
237
- }).pipe(
238
- takeUntil(destroy$),
239
- switchMap(async (d) => {
240
- const rootWidth = d.rootSize.width > 0
241
- ? d.rootSize.width
242
- : CHART_WIDTH_DEFAULT
243
- const rootHeight = d.rootSize.height > 0
244
- ? d.rootSize.height
245
- : CHART_HEIGHT_DEFAULT
246
- return {
247
- width: rootWidth - d.mergedPadding.left - d.mergedPadding.right,
248
- height: rootHeight - d.mergedPadding.top - d.mergedPadding.bottom,
249
- top: d.mergedPadding.top,
250
- right: d.mergedPadding.right,
251
- bottom: d.mergedPadding.bottom,
252
- left: d.mergedPadding.left,
253
- rootWidth,
254
- rootHeight
255
- }
256
- }),
257
- shareReplay(1)
258
- )
259
- layout$.subscribe(d => {
260
- svgSelection
261
- .attr('width', d.rootWidth)
262
- .attr('height', d.rootHeight)
263
- })
264
-
265
- // -- computedData --
266
- const computedData$: Observable<ComputedDataTypeMap<T>> = combineLatest({
267
- data: sharedData$,
268
- dataFormatter: shareAndMergedDataFormatter$,
269
- chartParams: shareAndMergedChartParams$,
270
- // layout: iif(() => isAxesTypeMap[chartType] === true, layout$, of(undefined))
271
- }).pipe(
272
- takeUntil(destroy$),
273
- switchMap(async d => d),
274
- switchMap((d) => {
275
- return of(d)
276
- .pipe(
277
- map(_d => {
278
- try {
279
- return computedDataFn({ data: _d.data, dataFormatter: _d.dataFormatter, chartParams: _d.chartParams })
280
- } catch (e) {
281
- console.error(e)
282
- throw new Error(e)
283
- }
284
- }),
285
- catchError(() => EMPTY)
286
- )
287
- }),
288
- shareReplay(1)
289
- )
290
-
291
- // subscribe - computedData組合了所有的chart參數,所以訂閱computedData可以一次訂閱所有的資料流
292
- computedData$.subscribe()
293
-
294
- // -- plugins --
295
- const pluginEntityMap: any = {} // 用於destroy
296
- chartSubject.plugins$.subscribe(plugins => {
297
- if (!plugins) {
298
- return
299
- }
300
- // 建立<g>
301
- const update = selectionPlugins
302
- .selectAll<SVGGElement, PluginEntity<T, any, any>>('g.orbcharts__plugin')
303
- .data(plugins, d => d.name as string)
304
- const enter = update.enter()
305
- .append('g')
306
- .attr('class', plugin => {
307
- return `orbcharts__plugin orbcharts__${plugin.name}`
308
- })
309
- const exit = update.exit()
310
- .remove()
311
-
312
- // destroy entity
313
- exit.each((plugin: PluginEntity<T, unknown, unknown>, i, n) => {
314
- if (pluginEntityMap[plugin.name as string]) {
315
- pluginEntityMap[plugin.name as string].destroy()
316
- }
317
- })
318
-
319
- enter.each((plugin, i, n) => {
320
- const _pluginObserverBase = {
321
- fullParams$: new Observable(),
322
- fullChartParams$: shareAndMergedChartParams$,
323
- fullDataFormatter$: shareAndMergedDataFormatter$,
324
- computedData$,
325
- layout$
326
- }
327
- const pluginObserver: ContextObserverTypeMap<T, typeof plugin.defaultParams> = contextObserverFn({
328
- observer: _pluginObserverBase,
329
- subject: chartSubject
330
- })
331
-
332
- // -- createPlugin(plugin) --
333
- const pluginSelection = d3.select(n[i])
334
- const pluginContext: PluginContext<T, typeof plugin.name, typeof plugin.defaultParams> = {
335
- selection: pluginSelection,
336
- rootSelection: svgSelection,
337
- name: plugin.name,
338
- chartType,
339
- subject: chartSubject,
340
- observer: pluginObserver
341
- }
342
-
343
- plugin.setPresetParams(mergedPresetWithDefault.allPluginParams[plugin.name] ?? {})
344
- // 傳入context
345
- plugin.setContext(pluginContext)
346
-
347
- // 紀錄起來
348
- pluginEntityMap[pluginContext.name as string] = plugin
349
-
350
- // init plugin
351
- plugin.init()
352
-
353
- })
354
-
355
- })
356
-
357
- return {
358
- ...chartSubject,
359
- selection: svgSelection,
360
- destroy () {
361
- d3.select(element).selectAll('svg').remove()
362
- destroy$.next(undefined)
363
- rootSizeSubscription.unsubscribe()
364
- }
365
- }
366
- }
367
- }
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> = ((options) => {
141
+ const _options = options ? options : CHART_OPTIONS_DEFAULT as ChartOptionsPartial<T>
142
+ const preset = _options.preset ? _options.preset : {} as PresetPartial<T>
143
+
144
+ return {
145
+ chartParams: preset.chartParams
146
+ ? mergeOptionsWithDefault(preset.chartParams, CHART_PARAMS_DEFAULT)
147
+ : CHART_PARAMS_DEFAULT,
148
+ dataFormatter: preset.dataFormatter
149
+ // ? mergeOptionsWithDefault(preset.dataFormatter, defaultDataFormatter)
150
+ ? mergeDataFormatter(preset.dataFormatter, defaultDataFormatter, chartType)
151
+ : defaultDataFormatter,
152
+ allPluginParams: preset.allPluginParams
153
+ ? preset.allPluginParams
154
+ : {},
155
+ description: preset.description ?? ''
156
+ }
157
+ })(options)
158
+
159
+ const sharedData$ = chartSubject.data$.pipe(shareReplay(1))
160
+ const shareAndMergedDataFormatter$ = chartSubject.dataFormatter$
161
+ .pipe(
162
+ takeUntil(destroy$),
163
+ startWith({}),
164
+ map((dataFormatter) => {
165
+ // const mergedData = mergeOptionsWithDefault(dataFormatter, mergedPresetWithDefault.dataFormatter)
166
+
167
+ // if (chartType === 'multiGrid' && (dataFormatter as DataFormatterPartialTypeMap<'multiGrid'>).gridList != null) {
168
+ // // multiGrid欄位為陣列,需要各別來merge預設值
169
+ // (mergedData as DataFormatterTypeMap<'multiGrid'>).gridList = (dataFormatter as DataFormatterPartialTypeMap<'multiGrid'>).gridList.map(d => {
170
+ // return mergeOptionsWithDefault(d, (mergedPresetWithDefault.dataFormatter as DataFormatterTypeMap<'multiGrid'>).gridList[0])
171
+ // })
172
+ // }
173
+ // return mergedData
174
+ return mergeDataFormatter(dataFormatter, mergedPresetWithDefault.dataFormatter, chartType)
175
+ }),
176
+ shareReplay(1)
177
+ )
178
+ const shareAndMergedChartParams$ = chartSubject.chartParams$
179
+ .pipe(
180
+ takeUntil(destroy$),
181
+ startWith({}),
182
+ map((d) => {
183
+ return mergeOptionsWithDefault(d, mergedPresetWithDefault.chartParams)
184
+ }),
185
+ shareReplay(1)
186
+ )
187
+
188
+ // -- size --
189
+ // padding
190
+ const mergedPadding$ = shareAndMergedChartParams$
191
+ .pipe(
192
+ takeUntil(destroy$),
193
+ startWith({}),
194
+ map((d: any) => {
195
+ return mergeOptionsWithDefault(d.padding ?? {}, PADDING_DEFAULT)
196
+ })
197
+ )
198
+ mergedPadding$
199
+ .pipe(
200
+ takeUntil(destroy$),
201
+ first()
202
+ )
203
+ .subscribe(d => {
204
+ selectionLayout
205
+ .attr('transform', `translate(${d.left}, ${d.top})`)
206
+ })
207
+ mergedPadding$.subscribe(size => {
208
+ selectionLayout
209
+ .transition()
210
+ .attr('transform', `translate(${size.left}, ${size.top})`)
211
+ })
212
+
213
+ // 監聽外層的element尺寸
214
+ const rootSize$ = resizeObservable(element)
215
+ .pipe(
216
+ takeUntil(destroy$),
217
+ share()
218
+ )
219
+ const rootSizeFiltered$ = of().pipe(
220
+ mergeWith(
221
+ rootSize$.pipe(
222
+ debounceTime(250)
223
+ ),
224
+ rootSize$.pipe(
225
+ throttleTime(250)
226
+ )
227
+ ),
228
+ distinctUntilChanged((a, b) => JSON.stringify(a) === JSON.stringify(b)),
229
+ share()
230
+ )
231
+ const rootSizeSubscription = rootSizeFiltered$.subscribe()
232
+
233
+ // layout
234
+ const layout$: Observable<Layout> = combineLatest({
235
+ rootSize: rootSizeFiltered$,
236
+ mergedPadding: mergedPadding$
237
+ }).pipe(
238
+ takeUntil(destroy$),
239
+ switchMap(async (d) => {
240
+ const rootWidth = d.rootSize.width > 0
241
+ ? d.rootSize.width
242
+ : CHART_WIDTH_DEFAULT
243
+ const rootHeight = d.rootSize.height > 0
244
+ ? d.rootSize.height
245
+ : CHART_HEIGHT_DEFAULT
246
+ return {
247
+ width: rootWidth - d.mergedPadding.left - d.mergedPadding.right,
248
+ height: rootHeight - d.mergedPadding.top - d.mergedPadding.bottom,
249
+ top: d.mergedPadding.top,
250
+ right: d.mergedPadding.right,
251
+ bottom: d.mergedPadding.bottom,
252
+ left: d.mergedPadding.left,
253
+ rootWidth,
254
+ rootHeight
255
+ }
256
+ }),
257
+ shareReplay(1)
258
+ )
259
+ layout$.subscribe(d => {
260
+ svgSelection
261
+ .attr('width', d.rootWidth)
262
+ .attr('height', d.rootHeight)
263
+ })
264
+
265
+ // -- computedData --
266
+ const computedData$: Observable<ComputedDataTypeMap<T>> = combineLatest({
267
+ data: sharedData$,
268
+ dataFormatter: shareAndMergedDataFormatter$,
269
+ chartParams: shareAndMergedChartParams$,
270
+ // layout: iif(() => isAxesTypeMap[chartType] === true, layout$, of(undefined))
271
+ }).pipe(
272
+ takeUntil(destroy$),
273
+ switchMap(async d => d),
274
+ switchMap((d) => {
275
+ return of(d)
276
+ .pipe(
277
+ map(_d => {
278
+ try {
279
+ return computedDataFn({ data: _d.data, dataFormatter: _d.dataFormatter, chartParams: _d.chartParams })
280
+ } catch (e) {
281
+ console.error(e)
282
+ throw new Error(e)
283
+ }
284
+ }),
285
+ catchError(() => EMPTY)
286
+ )
287
+ }),
288
+ shareReplay(1)
289
+ )
290
+
291
+ // subscribe - computedData組合了所有的chart參數,所以訂閱computedData可以一次訂閱所有的資料流
292
+ computedData$.subscribe()
293
+
294
+ // -- plugins --
295
+ const pluginEntityMap: any = {} // 用於destroy
296
+ chartSubject.plugins$.subscribe(plugins => {
297
+ if (!plugins) {
298
+ return
299
+ }
300
+ // 建立<g>
301
+ const update = selectionPlugins
302
+ .selectAll<SVGGElement, PluginEntity<T, any, any>>('g.orbcharts__plugin')
303
+ .data(plugins, d => d.name as string)
304
+ const enter = update.enter()
305
+ .append('g')
306
+ .attr('class', plugin => {
307
+ return `orbcharts__plugin orbcharts__${plugin.name}`
308
+ })
309
+ const exit = update.exit()
310
+ .remove()
311
+
312
+ // destroy entity
313
+ exit.each((plugin: PluginEntity<T, unknown, unknown>, i, n) => {
314
+ if (pluginEntityMap[plugin.name as string]) {
315
+ pluginEntityMap[plugin.name as string].destroy()
316
+ pluginEntityMap[plugin.name as string] = undefined
317
+ }
318
+ })
319
+
320
+ enter.each((plugin, i, n) => {
321
+ const _pluginObserverBase = {
322
+ fullParams$: new Observable(),
323
+ fullChartParams$: shareAndMergedChartParams$,
324
+ fullDataFormatter$: shareAndMergedDataFormatter$,
325
+ computedData$,
326
+ layout$
327
+ }
328
+ const pluginObserver: ContextObserverTypeMap<T, typeof plugin.defaultParams> = contextObserverFn({
329
+ observer: _pluginObserverBase,
330
+ subject: chartSubject
331
+ })
332
+
333
+ // -- createPlugin(plugin) --
334
+ const pluginSelection = d3.select(n[i])
335
+ const pluginContext: PluginContext<T, typeof plugin.name, typeof plugin.defaultParams> = {
336
+ selection: pluginSelection,
337
+ rootSelection: svgSelection,
338
+ name: plugin.name,
339
+ chartType,
340
+ subject: chartSubject,
341
+ observer: pluginObserver
342
+ }
343
+
344
+ plugin.setPresetParams(mergedPresetWithDefault.allPluginParams[plugin.name] ?? {})
345
+ // 傳入context
346
+ plugin.setContext(pluginContext)
347
+
348
+ // 紀錄起來
349
+ pluginEntityMap[pluginContext.name as string] = plugin
350
+
351
+ // init plugin
352
+ plugin.init()
353
+
354
+ })
355
+
356
+ })
357
+
358
+ return {
359
+ ...chartSubject,
360
+ selection: svgSelection,
361
+ destroy () {
362
+ d3.select(element).selectAll('svg').remove()
363
+ destroy$.next(undefined)
364
+ rootSizeSubscription.unsubscribe()
365
+ }
366
+ }
367
+ }
368
+ }