@orbcharts/core 3.0.7 → 4.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (198) hide show
  1. package/LICENSE +200 -200
  2. package/dist/orbcharts-core.es.js +2849 -6594
  3. package/dist/orbcharts-core.umd.js +6 -6
  4. package/dist/src/OrbCharts.d.ts +18 -0
  5. package/dist/src/chart/createChart.d.ts +3 -0
  6. package/dist/src/chart/createGraphData.d.ts +3 -0
  7. package/dist/src/chart/createGridData.d.ts +3 -0
  8. package/dist/src/chart/createMultivariateData.d.ts +3 -0
  9. package/dist/src/chart/createSeriesData.d.ts +3 -0
  10. package/dist/src/chart/createTreeData.d.ts +3 -0
  11. package/dist/src/chart/defaults.d.ts +5 -0
  12. package/dist/src/defineCanvasLayer.d.ts +16 -0
  13. package/dist/src/defineCanvasPlugin.d.ts +22 -0
  14. package/dist/src/defineSVGLayer.d.ts +16 -0
  15. package/dist/src/defineSVGPlugin.d.ts +22 -0
  16. package/dist/src/index.d.ts +6 -14
  17. package/dist/src/layer/createLayer.d.ts +3 -0
  18. package/dist/src/plugin/createPlugin.d.ts +3 -0
  19. package/dist/src/test/createGraphData.test.d.ts +1 -0
  20. package/dist/src/test/createTreeData.test.d.ts +1 -0
  21. package/dist/src/test/simple-graph-test.d.ts +74 -0
  22. package/dist/src/test/simple-tree-test.d.ts +13 -0
  23. package/dist/src/types/Chart.d.ts +39 -0
  24. package/dist/src/types/ChartContext.d.ts +27 -0
  25. package/dist/src/types/Common.d.ts +3 -0
  26. package/dist/src/types/Encoding.d.ts +33 -0
  27. package/dist/src/types/Event.d.ts +12 -0
  28. package/dist/src/types/Layers.d.ts +55 -0
  29. package/dist/src/types/ModelData.d.ts +70 -0
  30. package/dist/src/types/Plugin.d.ts +39 -0
  31. package/dist/src/types/RawData.d.ts +18 -0
  32. package/dist/src/types/RenderData.d.ts +4 -0
  33. package/dist/src/types/Theme.d.ts +17 -0
  34. package/dist/src/types/Validator.d.ts +20 -0
  35. package/dist/src/types/index.d.ts +12 -0
  36. package/dist/src/utils/aggregateUtils.d.ts +37 -0
  37. package/dist/src/utils/colorUtils.d.ts +22 -0
  38. package/dist/src/utils/commonUtils.d.ts +3 -5
  39. package/dist/src/utils/dom-lifecycle.d.ts +37 -0
  40. package/dist/src/utils/dom.d.ts +6 -0
  41. package/dist/src/utils/index.d.ts +5 -1
  42. package/dist/src/utils/observables.d.ts +1 -25
  43. package/dist/src/utils/orbchartsUtils.d.ts +2 -53
  44. package/dist/src/utils/validator.d.ts +2 -2
  45. package/dist/test/aggregateTest.d.ts +1 -0
  46. package/package.json +24 -13
  47. package/src/OrbCharts.ts +35 -0
  48. package/src/chart/createChart.ts +1014 -0
  49. package/src/chart/createGraphData.ts +391 -0
  50. package/src/chart/createGridData.ts +247 -0
  51. package/src/chart/createMultivariateData.ts +181 -0
  52. package/src/chart/createSeriesData.ts +297 -0
  53. package/src/chart/createTreeData.ts +344 -0
  54. package/src/chart/defaults.ts +120 -0
  55. package/src/defineCanvasLayer.ts +24 -0
  56. package/src/defineCanvasPlugin.ts +39 -0
  57. package/src/defineSVGLayer.ts +24 -0
  58. package/src/defineSVGPlugin.ts +39 -0
  59. package/src/index.ts +8 -20
  60. package/src/layer/createLayer.ts +138 -0
  61. package/src/plugin/createPlugin.ts +488 -0
  62. package/src/test/createGraphData.test.ts +103 -0
  63. package/src/test/createTreeData.test.ts +97 -0
  64. package/src/test/simple-graph-test.js +51 -0
  65. package/src/test/simple-tree-test.js +58 -0
  66. package/src/types/Chart.ts +62 -0
  67. package/src/types/ChartContext.ts +42 -0
  68. package/src/types/Common.ts +5 -0
  69. package/src/types/Encoding.ts +43 -0
  70. package/src/types/Event.ts +26 -0
  71. package/src/types/Layers.ts +93 -0
  72. package/src/types/ModelData.ts +95 -0
  73. package/src/types/Plugin.ts +101 -0
  74. package/src/types/RawData.ts +67 -0
  75. package/src/types/RenderData.ts +16 -0
  76. package/src/types/Theme.ts +21 -0
  77. package/src/types/Validator.ts +36 -0
  78. package/src/types/index.ts +12 -0
  79. package/src/utils/aggregateUtils.ts +99 -0
  80. package/src/utils/colorUtils.ts +63 -0
  81. package/src/utils/commonUtils.ts +56 -55
  82. package/src/utils/dom-lifecycle.ts +164 -0
  83. package/src/utils/dom.ts +55 -0
  84. package/src/utils/errorMessage.ts +40 -40
  85. package/src/utils/index.ts +8 -4
  86. package/src/utils/observables.ts +16 -307
  87. package/src/utils/orbchartsUtils.ts +9 -396
  88. package/src/utils/validator.ts +127 -126
  89. package/dist/lib/core-types.d.ts +0 -1
  90. package/dist/src/AbstractChart.d.ts +0 -19
  91. package/dist/src/GridChart.d.ts +0 -6
  92. package/dist/src/MultiGridChart.d.ts +0 -6
  93. package/dist/src/MultiValueChart.d.ts +0 -6
  94. package/dist/src/RelationshipChart.d.ts +0 -6
  95. package/dist/src/SeriesChart.d.ts +0 -6
  96. package/dist/src/TreeChart.d.ts +0 -6
  97. package/dist/src/base/createBaseChart.d.ts +0 -3
  98. package/dist/src/base/createBasePlugin.d.ts +0 -3
  99. package/dist/src/base/validators/chartOptionsValidator.d.ts +0 -3
  100. package/dist/src/base/validators/chartParamsValidator.d.ts +0 -3
  101. package/dist/src/base/validators/elementValidator.d.ts +0 -3
  102. package/dist/src/base/validators/pluginsValidator.d.ts +0 -3
  103. package/dist/src/defaults.d.ts +0 -25
  104. package/dist/src/defineGridPlugin.d.ts +0 -1
  105. package/dist/src/defineMultiGridPlugin.d.ts +0 -1
  106. package/dist/src/defineMultiValuePlugin.d.ts +0 -1
  107. package/dist/src/defineNoneDataPlugin.d.ts +0 -1
  108. package/dist/src/defineRelationshipPlugin.d.ts +0 -1
  109. package/dist/src/defineSeriesPlugin.d.ts +0 -1
  110. package/dist/src/defineTreePlugin.d.ts +0 -1
  111. package/dist/src/grid/computedDataFn.d.ts +0 -4
  112. package/dist/src/grid/contextObserverCallback.d.ts +0 -3
  113. package/dist/src/grid/dataFormatterValidator.d.ts +0 -3
  114. package/dist/src/grid/dataValidator.d.ts +0 -3
  115. package/dist/src/grid/gridObservables.d.ts +0 -64
  116. package/dist/src/multiGrid/computedDataFn.d.ts +0 -3
  117. package/dist/src/multiGrid/contextObserverCallback.d.ts +0 -3
  118. package/dist/src/multiGrid/dataFormatterValidator.d.ts +0 -3
  119. package/dist/src/multiGrid/dataValidator.d.ts +0 -3
  120. package/dist/src/multiGrid/multiGridObservables.d.ts +0 -16
  121. package/dist/src/multiValue/computedDataFn.d.ts +0 -3
  122. package/dist/src/multiValue/contextObserverCallback.d.ts +0 -3
  123. package/dist/src/multiValue/dataFormatterValidator.d.ts +0 -3
  124. package/dist/src/multiValue/dataValidator.d.ts +0 -3
  125. package/dist/src/multiValue/multiValueObservables.d.ts +0 -130
  126. package/dist/src/relationship/computedDataFn.d.ts +0 -3
  127. package/dist/src/relationship/contextObserverCallback.d.ts +0 -3
  128. package/dist/src/relationship/dataFormatterValidator.d.ts +0 -3
  129. package/dist/src/relationship/dataValidator.d.ts +0 -3
  130. package/dist/src/relationship/relationshipObservables.d.ts +0 -13
  131. package/dist/src/series/computedDataFn.d.ts +0 -3
  132. package/dist/src/series/contextObserverCallback.d.ts +0 -3
  133. package/dist/src/series/dataFormatterValidator.d.ts +0 -3
  134. package/dist/src/series/dataValidator.d.ts +0 -3
  135. package/dist/src/series/seriesObservables.d.ts +0 -37
  136. package/dist/src/tree/computedDataFn.d.ts +0 -3
  137. package/dist/src/tree/contextObserverCallback.d.ts +0 -3
  138. package/dist/src/tree/dataFormatterValidator.d.ts +0 -3
  139. package/dist/src/tree/dataValidator.d.ts +0 -3
  140. package/dist/src/tree/treeObservables.d.ts +0 -10
  141. package/dist/src/utils/d3Scale.d.ts +0 -28
  142. package/lib/core-types.ts +0 -7
  143. package/src/AbstractChart.ts +0 -57
  144. package/src/GridChart.ts +0 -25
  145. package/src/MultiGridChart.ts +0 -25
  146. package/src/MultiValueChart.ts +0 -25
  147. package/src/RelationshipChart.ts +0 -25
  148. package/src/SeriesChart.ts +0 -25
  149. package/src/TreeChart.ts +0 -25
  150. package/src/base/createBaseChart.ts +0 -524
  151. package/src/base/createBasePlugin.ts +0 -154
  152. package/src/base/validators/chartOptionsValidator.ts +0 -24
  153. package/src/base/validators/chartParamsValidator.ts +0 -134
  154. package/src/base/validators/elementValidator.ts +0 -14
  155. package/src/base/validators/pluginsValidator.ts +0 -15
  156. package/src/defaults.ts +0 -284
  157. package/src/defineGridPlugin.ts +0 -3
  158. package/src/defineMultiGridPlugin.ts +0 -3
  159. package/src/defineMultiValuePlugin.ts +0 -3
  160. package/src/defineNoneDataPlugin.ts +0 -4
  161. package/src/defineRelationshipPlugin.ts +0 -3
  162. package/src/defineSeriesPlugin.ts +0 -3
  163. package/src/defineTreePlugin.ts +0 -3
  164. package/src/grid/computedDataFn.ts +0 -129
  165. package/src/grid/contextObserverCallback.ts +0 -209
  166. package/src/grid/dataFormatterValidator.ts +0 -126
  167. package/src/grid/dataValidator.ts +0 -13
  168. package/src/grid/gridObservables.ts +0 -699
  169. package/src/multiGrid/computedDataFn.ts +0 -123
  170. package/src/multiGrid/contextObserverCallback.ts +0 -109
  171. package/src/multiGrid/dataFormatterValidator.ts +0 -121
  172. package/src/multiGrid/dataValidator.ts +0 -13
  173. package/src/multiGrid/multiGridObservables.ts +0 -367
  174. package/src/multiValue/computedDataFn.ts +0 -113
  175. package/src/multiValue/contextObserverCallback.ts +0 -328
  176. package/src/multiValue/dataFormatterValidator.ts +0 -95
  177. package/src/multiValue/dataValidator.ts +0 -13
  178. package/src/multiValue/multiValueObservables.ts +0 -865
  179. package/src/relationship/computedDataFn.ts +0 -159
  180. package/src/relationship/contextObserverCallback.ts +0 -80
  181. package/src/relationship/dataFormatterValidator.ts +0 -14
  182. package/src/relationship/dataValidator.ts +0 -14
  183. package/src/relationship/relationshipObservables.ts +0 -85
  184. package/src/series/computedDataFn.ts +0 -88
  185. package/src/series/contextObserverCallback.ts +0 -132
  186. package/src/series/dataFormatterValidator.ts +0 -47
  187. package/src/series/dataValidator.ts +0 -13
  188. package/src/series/seriesObservables.ts +0 -210
  189. package/src/tree/computedDataFn.ts +0 -129
  190. package/src/tree/contextObserverCallback.ts +0 -58
  191. package/src/tree/dataFormatterValidator.ts +0 -14
  192. package/src/tree/dataValidator.ts +0 -14
  193. package/src/tree/treeObservables.ts +0 -106
  194. package/src/utils/d3Scale.ts +0 -198
  195. package/tsconfig.base.json +0 -14
  196. package/tsconfig.json +0 -3
  197. package/vite-env.d.ts +0 -7
  198. package/vite.config.js +0 -23
@@ -0,0 +1,138 @@
1
+ import {
2
+ Subject,
3
+ BehaviorSubject,
4
+ filter,
5
+ takeUntil,
6
+ switchMap,
7
+ combineLatest,
8
+ of,
9
+ map,
10
+ shareReplay,
11
+ debounceTime,
12
+ } from "rxjs"
13
+ import type {
14
+ DefineLayerConfig,
15
+ LayerEntity,
16
+ ExtendableContext,
17
+ LayerEnableProps
18
+ } from "../types"
19
+ import { deepOverwrite } from "../utils/commonUtils"
20
+ import { createOrbChartsErrorMessage, createValidatorErrorMessage, createValidatorWarningMessage } from "../utils"
21
+
22
+ export const createLayer = <
23
+ ExtendContext extends ExtendableContext,
24
+ PluginParams extends Record<string, any>,
25
+ LayerParams extends Record<string, any>
26
+ >(elementType: 'canvas' | 'svg', config: DefineLayerConfig<'svg' | 'canvas', ExtendContext, PluginParams, LayerParams>): LayerEntity<ExtendContext, PluginParams, LayerParams> => {
27
+
28
+ // let svgElement: SVGSVGElement | null = null
29
+ // let canvasElement: HTMLCanvasElement | null = null
30
+
31
+ const layerParams$ = new BehaviorSubject<LayerParams>(Object.assign({}, config.defaultParams))
32
+
33
+ // let _context: ChartContext<ExtendContext> = {} as ChartContext<ExtendContext>
34
+ let destroySetup = () => {}
35
+
36
+ const enableProps$ = new BehaviorSubject<LayerEnableProps<'svg' | 'canvas', ExtendContext, PluginParams, LayerParams> | null>(null)
37
+
38
+ const enabledProps$ = enableProps$.pipe(
39
+ filter(enableProps => enableProps !== null)
40
+ )
41
+
42
+ // show
43
+ combineLatest({
44
+ layerParams: layerParams$,
45
+ enabledProps: enabledProps$
46
+ }).pipe(
47
+ debounceTime(0)
48
+ ).subscribe(({ layerParams, enabledProps }) => {
49
+ destroySetup()
50
+ destroySetup = elementType === 'svg' ?
51
+ config.setup({
52
+ svgG: (enabledProps as LayerEnableProps<'svg', ExtendContext, PluginParams, LayerParams>).svgG,
53
+ // canvas: enabledProps.canvas,
54
+ // context: Object.assign({}, enabledProps.context),
55
+ context: enabledProps.context,
56
+ pluginParams$: enabledProps.pluginParams$,
57
+ layerParams$: layerParams$.pipe(
58
+ map(params => {
59
+ return deepOverwrite(params, enabledProps.initLayerParams ?? {})
60
+ }),
61
+ )
62
+ })
63
+ : config.setup({
64
+ // svgG: enabledProps.svgG,
65
+ canvas: (enabledProps as LayerEnableProps<'canvas', ExtendContext, PluginParams, LayerParams>).canvas,
66
+ // context: Object.assign({}, enabledProps.context),
67
+ context: enabledProps.context,
68
+ pluginParams$: enabledProps.pluginParams$,
69
+ layerParams$: layerParams$.pipe(
70
+ map(params => {
71
+ return deepOverwrite(params, enabledProps.initLayerParams ?? {})
72
+ }),
73
+ )
74
+ })
75
+ })
76
+
77
+ // hide
78
+ enableProps$.pipe(filter(enableProps => enableProps === null)).subscribe(() => {
79
+ destroySetup()
80
+ })
81
+
82
+ return {
83
+ _name: config.name,
84
+ _defaultParams: config.defaultParams,
85
+ _layerIndex: config.layerIndex,
86
+ _initShow: config.initShow,
87
+ _enable: (enableProps) => {
88
+ enableProps$.next(enableProps)
89
+ },
90
+ _disable: () => {
91
+ enableProps$.next(null)
92
+ },
93
+ // setParams: (partial) => {
94
+ // previousParams$.next(deepOverwrite(defaultParams$.getValue(), partial))
95
+ // },
96
+ _updateParams: (patch) => {
97
+ try {
98
+ // 檢查 data$ 資料格式是否正確
99
+ const { status, columnName, expectToBe } = config.validator(patch)
100
+ if (status === 'error') {
101
+ throw new Error(createValidatorErrorMessage({
102
+ columnName,
103
+ expectToBe,
104
+ from: `${config.name}.params$`
105
+ }))
106
+ } else if (status === 'warning') {
107
+ console.warn(createValidatorWarningMessage({
108
+ columnName,
109
+ expectToBe,
110
+ from: `${config.name}.params$`
111
+ }))
112
+ }
113
+ } catch (e) {
114
+ // throw new Error(e.message)
115
+ // 驗證失敗仍繼續執行,才不會把 Observable 資料流給中斷掉
116
+ console.error(createOrbChartsErrorMessage(e))
117
+ }
118
+ const layerParams = deepOverwrite(layerParams$.getValue(), patch)
119
+ layerParams$.next(layerParams)
120
+ },
121
+ _forceReplaceParams: (full) => {
122
+ layerParams$.next(full)
123
+ },
124
+ _getParams: () => {
125
+ return layerParams$.getValue()
126
+ },
127
+ // injectContext: (context) => {
128
+ // _context = Object.assign({}, context)
129
+ // // re-setup layer with new context
130
+ // enableProps$.next(true)
131
+ // },
132
+ _destroy: () => {
133
+ enableProps$.next(null)
134
+ enableProps$.complete()
135
+ layerParams$.complete()
136
+ }
137
+ }
138
+ }
@@ -0,0 +1,488 @@
1
+ import type { ChartContext, DeepPartial, DefinePluginConfig, ExtendableContext, PluginEntity } from '../types'
2
+ import {
3
+ BehaviorSubject,
4
+ combineLatest,
5
+ switchMap,
6
+ map,
7
+ filter,
8
+ of,
9
+ shareReplay,
10
+ debounceTime,
11
+ first,
12
+ Observable,
13
+ Subject,
14
+ } from 'rxjs'
15
+ import type { PluginSetupProps, LayerEnableProps } from '../types'
16
+ // import { handleElementLifecycle } from '../utils/dom-lifecycle'
17
+ // import { createSVG, createCanvasElement, createSVGGroup, createCanvas } from '../utils/dom'
18
+ // import { createPluginClassName, createLayerClassName } from '../utils/orbchartsUtils'
19
+ import { deepOverwrite } from '../utils/commonUtils'
20
+ import { createOrbChartsErrorMessage, createValidatorErrorMessage, createValidatorWarningMessage } from '../utils/errorMessage'
21
+
22
+ export const createPlugin = <
23
+ ElementType extends 'svg' | 'canvas',
24
+ ExtendContext extends ExtendableContext,
25
+ PluginParams,
26
+ AllLayerParams
27
+ >(elementType: ElementType, config: DefinePluginConfig<ExtendContext, PluginParams, AllLayerParams>, initPluginParams?: DeepPartial<PluginParams | AllLayerParams>): PluginEntity<ElementType, PluginParams, AllLayerParams> => {
28
+ // const svgClassName = `${createPluginClassName(config.name)}__svg`
29
+ // const svgGClassName = `${createPluginClassName(config.name)}__g`
30
+ // const canvasClassName = `${createPluginClassName(config.name)}__canvas`
31
+
32
+ // 內部保存起來的 id
33
+ let _id = config.name
34
+
35
+ let destroySetup = () => {}
36
+
37
+ const context$ = new BehaviorSubject<ChartContext<ExtendContext> | null>(null)
38
+ const injectedContext$ = context$.pipe(
39
+ filter((context): context is ChartContext<ExtendContext> => context !== null)
40
+ )
41
+
42
+ // element 全部保存起來避免重複創建
43
+ // const layerSVGElementsRef: Record<string, SVGElement> = {}
44
+ // const layerCanvasElementsRef: Record<string, HTMLCanvasElement> = {}
45
+
46
+ // 每個 plugin 實例建立「自己的」layer 實體。
47
+ // Layer 內部狀態(enableProps$、setup 訂閱等)一次只能服務一個 plugin 實例,
48
+ // 若跨實例共用(例如 module 層級的單例),同頁多個 plugin 會互搶 layer,
49
+ // 造成部分圖表的 layer 不繪製或被銷毀。
50
+ const layers = config.layers.map((Layer) => new Layer())
51
+
52
+ // 全部的layers
53
+ const allLayerInstances = {
54
+ // 將陣列轉成字典
55
+ ...layers.reduce((acc, layer) => {
56
+ acc[layer._name as keyof AllLayerParams] = layer
57
+ return acc
58
+ }, {} as Record<keyof AllLayerParams, typeof layers[number]>)
59
+ }
60
+ const getAllLayerNamesSet = () => new Set(layers.map(l => l._name as keyof AllLayerParams))
61
+ // const getParamsKeySet = (params: DeepPartial<PluginParams | AllLayerParams>): Set<keyof AllLayerParams> => {
62
+ // // 取得有設定 params 的 layer name
63
+ // if (params) {
64
+ // const AllLayerNamesSet = getAllLayerNamesSet()
65
+ // const keysOfParams = Object.keys(params) as (keyof AllLayerParams)[]
66
+ // return new Set(keysOfParams.filter(key => AllLayerNamesSet.has(key)))
67
+ // }
68
+ // return new Set()
69
+ // }
70
+ const getParamsKeys = (params: DeepPartial<PluginParams | AllLayerParams>): (keyof AllLayerParams)[] => {
71
+ // 取得有設定 params 的 layer name
72
+ if (params) {
73
+ const AllLayerNamesSet = getAllLayerNamesSet()
74
+ const keysOfParams = Object.keys(params) as (keyof AllLayerParams)[]
75
+ return keysOfParams.filter(key => AllLayerNamesSet.has(key))
76
+ }
77
+ return []
78
+ }
79
+
80
+ // config 設定顯示的 layers
81
+ const systemInitLayerNames = layers.reduce((prev, current) => {
82
+ if (current._initShow) {
83
+ prev.push(current._name as keyof AllLayerParams)
84
+ }
85
+ return prev
86
+ }, [] as (keyof AllLayerParams)[])
87
+ // 有設定 params 對應的 layers 名稱
88
+ const initLayerNames = getParamsKeys(initPluginParams)
89
+ // const InitLayerNameSet$ = new BehaviorSubject<Set<keyof AllLayerParams>>(getParamsKeySet(initPluginParams))
90
+ // 有設定 show/showOnly 等的 layers 名稱
91
+ const ShownLayerNameSet$ = new BehaviorSubject<Set<keyof AllLayerParams>>(
92
+ new Set(initLayerNames.length > 0 ? initLayerNames : systemInitLayerNames)
93
+ )
94
+ // 顯示的 layers 名稱
95
+ // const getShownLayerNames = ({ InitLayerNameSet, IsShowLayerNameSet }: { InitLayerNameSet: Set<keyof AllLayerParams>, IsShowLayerNameSet: Set<keyof AllLayerParams> | null }) => {
96
+ // const ShownLayerNameSet = IsShowLayerNameSet == null ? InitLayerNameSet : IsShowLayerNameSet // IsShowLayerNameSet 預設為 null
97
+ // return ShownLayerNameSet
98
+ // }
99
+ // const ShownLayerNameSet$ = combineLatest({
100
+ // InitLayerNameSet: InitLayerNameSet$,
101
+ // IsShowLayerNameSet: IsShowLayerNameSet$
102
+ // }).pipe(
103
+ // debounceTime(0),
104
+ // // filter(({ context }) => context !== null),
105
+ // map(({ InitLayerNameSet, IsShowLayerNameSet }) => {
106
+ // return getShownLayerNames({ InitLayerNameSet, IsShowLayerNameSet })
107
+ // })
108
+ // )
109
+ // 顯示的 layers 名稱依照 layerIndex 排序
110
+ const shownLayerNamesSeq$ = ShownLayerNameSet$.pipe(
111
+ map(ShownLayerNameSet => {
112
+ // 依照 layerIndex 排序
113
+ const shownLayerNamesSeq: string[] = Array.from(ShownLayerNameSet)
114
+ .map(name => [
115
+ name,
116
+ layers.find(l => l._name === name)?._layerIndex ?? -1
117
+ ])
118
+ .filter(([, index]) => index !== -1)
119
+ .sort((a, b) => (a[1] as number) - (b[1] as number))
120
+ .map(([name]) => name as string)
121
+ return shownLayerNamesSeq
122
+ })
123
+ )
124
+
125
+ // plugin 的根元素
126
+ // const pluginElement$: Observable<SVGGElement | HTMLCanvasElement> = injectedContext$.pipe(
127
+ // map(context => {
128
+ // // -- 在 svg 或 canvas 元素底下建立 plugin 的元素 --
129
+ // if (elementType === 'svg') {
130
+ // let svgGElement: SVGGElement | null = context.svg.querySelector(`.${svgGClassName}`)
131
+ // if (!svgGElement) {
132
+ // svgGElement = createSVGGroup(svgGClassName)
133
+ // context.svg.appendChild(svgGElement)
134
+ // }
135
+ // return svgGElement
136
+ // } else if (elementType === 'canvas') {
137
+ // let canvasElement: HTMLCanvasElement | null = context.canvas.querySelector(`.${canvasClassName}`)
138
+ // if (!canvasElement) {
139
+ // canvasElement = createCanvasElement(canvasClassName)
140
+ // context.canvas.appendChild(canvasElement)
141
+ // }
142
+ // return canvasElement
143
+ // }
144
+ // })
145
+ // )
146
+
147
+ // 目前生效的 plugin 本身 params(預設值 + 歷次 patch 累積合併)。
148
+ // deepOverwrite 會忽略 defaults 中不存在的 key,因此建構子參數內的
149
+ // layer keys(如 { Bar: {} })會被自然濾除,不會混入 plugin-level params。
150
+ let currentPluginParams: PluginParams = deepOverwrite(
151
+ (config.defaultParams ?? {}) as PluginParams,
152
+ (initPluginParams ?? {}) as DeepPartial<PluginParams>
153
+ )
154
+
155
+ // update plugin params
156
+ const patchPluginParams$ = new BehaviorSubject<DeepPartial<AllLayerParams | PluginParams> | undefined>(
157
+ initPluginParams ?? {}
158
+ )
159
+
160
+ // force replace plugin params
161
+ const forceReplacePluginParams$ = new Subject<DeepPartial<AllLayerParams | PluginParams> | undefined>()
162
+
163
+ const pluginParams$ = new Observable<PluginParams>(subscriber => {
164
+ // -- update --
165
+ patchPluginParams$.subscribe(patch => {
166
+ try {
167
+ // 檢查 data$ 資料格式是否正確
168
+ const { status, columnName, expectToBe } = config.validator(patch)
169
+ if (status === 'error') {
170
+ throw new Error(createValidatorErrorMessage({
171
+ columnName,
172
+ expectToBe,
173
+ from: `${config.name}.params$`
174
+ }))
175
+ } else if (status === 'warning') {
176
+ console.warn(createValidatorWarningMessage({
177
+ columnName,
178
+ expectToBe,
179
+ from: `${config.name}.params$`
180
+ }))
181
+ }
182
+ } catch (e) {
183
+ // throw new Error(e.message)
184
+ // 驗證失敗仍繼續執行,才不會把 Observable 資料流給中斷掉
185
+ console.error(createOrbChartsErrorMessage(e))
186
+ }
187
+
188
+ // currentPluginParams 已於 updateParams 中累積合併(歷次 patch 不互相覆蓋)
189
+ subscriber.next(currentPluginParams)
190
+ })
191
+
192
+ // -- force replace --
193
+ forceReplacePluginParams$.subscribe(full => {
194
+ subscriber.next(full as PluginParams)
195
+ })
196
+ })
197
+
198
+ // patchPluginParams$.pipe(
199
+ // map(patch => {
200
+ // try {
201
+ // // 檢查 data$ 資料格式是否正確
202
+ // const { status, columnName, expectToBe } = config.validator(patch)
203
+ // if (status === 'error') {
204
+ // throw new Error(createValidatorErrorMessage({
205
+ // columnName,
206
+ // expectToBe,
207
+ // from: `${config.name}.params$`
208
+ // }))
209
+ // } else if (status === 'warning') {
210
+ // console.warn(createValidatorWarningMessage({
211
+ // columnName,
212
+ // expectToBe,
213
+ // from: `${config.name}.params$`
214
+ // }))
215
+ // }
216
+ // } catch (e) {
217
+ // // throw new Error(e.message)
218
+ // // 驗證失敗仍繼續執行,才不會把 Observable 資料流給中斷掉
219
+ // console.error(createOrbChartsErrorMessage(e))
220
+ // }
221
+
222
+ // return deepOverwrite(config.defaultParams, patch as DeepPartial<PluginParams> ?? {})
223
+ // }),
224
+ // shareReplay(1)
225
+ // )
226
+
227
+ const pluginSetupProps$: Observable<PluginSetupProps<ExtendContext, PluginParams>> = injectedContext$.pipe(
228
+ map((context) => {
229
+ // const pluginSetupProps: PluginSetupProps<ExtendContext, PluginParams> =
230
+ // elementType === 'svg' ? {
231
+ // context: context as ChartContext<ExtendContext>, // 初始化時 context 有可能被 in place 擴展
232
+ // svgG: pluginElement as SVGSVGElement,
233
+ // pluginParams$
234
+ // } : {
235
+ // context: context as ChartContext<ExtendContext>,
236
+ // canvas: pluginElement as HTMLCanvasElement,
237
+ // pluginParams$
238
+ // }
239
+ return {
240
+ context, // 初始化時 context 有可能被 in place 擴展
241
+ pluginParams$
242
+ }
243
+ }),
244
+ first() // 只做初始化
245
+ )
246
+
247
+ combineLatest({
248
+ pluginSetupProps: pluginSetupProps$,
249
+ ShownLayerNameSet: ShownLayerNameSet$,
250
+ shownLayerNamesSeq: shownLayerNamesSeq$
251
+ }).pipe(
252
+ debounceTime(0)
253
+ ).subscribe(({ pluginSetupProps, ShownLayerNameSet, shownLayerNamesSeq }) => {
254
+
255
+ // 更新 layer elements
256
+ const layerElements = pluginSetupProps.context._updateLayerElements(
257
+ elementType,
258
+ _id,
259
+ shownLayerNamesSeq.map(layer => ({
260
+ pluginId: _id,
261
+ pluginName: config.name,
262
+ layerName: layer,
263
+ layerIndex: allLayerInstances[layer as keyof AllLayerParams]._layerIndex
264
+ }))
265
+ )
266
+
267
+ // init plugin
268
+ if (config.setup) {
269
+ destroySetup = config.setup(pluginSetupProps)
270
+ }
271
+
272
+ // // layer element - 處理 SVG 元素的 enter/update/exit
273
+ // if (elementType === 'svg') {
274
+ // handleElementLifecycle(
275
+ // // (pluginSetupProps as PluginSetupProps<ExtendContext, PluginParams>).svgG,
276
+ // pluginSetupProps.context.svg,
277
+ // shownLayerNamesSeq, // 依照 shownLayerNamesSeq
278
+ // layerSVGElementsRef,
279
+ // (layerName) => createSVGGroup(createLayerClassName(config.name, layerName))
280
+ // )
281
+ // }
282
+
283
+ // // layer element - 處理 Canvas 元素的 enter/update/exit
284
+ // if (elementType === 'canvas') {
285
+ // handleElementLifecycle(
286
+ // // (pluginSetupProps as PluginSetupProps<ExtendContext, PluginParams>).canvas,
287
+ // pluginSetupProps.context.canvas,
288
+ // shownLayerNamesSeq, // 依照 shownLayerNamesSeq
289
+ // layerCanvasElementsRef,
290
+ // (layerName) => createCanvas(createLayerClassName(config.name, layerName))
291
+ // )
292
+ // }
293
+
294
+ // init layers
295
+ layers.forEach((layer) => {
296
+ if (ShownLayerNameSet.has(layer._name as keyof AllLayerParams)) {
297
+ const layerEnableProps: LayerEnableProps<'svg' | 'canvas', ExtendContext, PluginParams, unknown> =
298
+ elementType === 'svg' ?
299
+ {
300
+ // svgG: (pluginSetupProps as PluginSetupProps<'svg', ExtendContext, PluginParams>).svgG.querySelector(`.${createLayerClassName(config.name, layer.name)}`),
301
+ svgG: layerElements[layer._name as keyof AllLayerParams] as SVGGElement,
302
+ // canvas: context.root.querySelector(`.${createLayerClassName(config.name, layer.name)}`),
303
+ // context: Object.assign({}, context) as ChartContext<ExtendContext>,
304
+ context: pluginSetupProps.context,
305
+ pluginParams$,
306
+ initLayerParams: patchPluginParams$.getValue()?.[layer._name as keyof AllLayerParams] as unknown || {}
307
+ }
308
+ : {
309
+ // svgG: context.root.querySelector(`.${createLayerClassName(config.name, layer.name)}`),
310
+ // canvas: (pluginSetupProps as PluginSetupProps<'canvas', ExtendContext, PluginParams>).canvas.querySelector(`.${createLayerClassName(config.name, layer.name)}`),
311
+ canvas: layerElements[layer._name as keyof AllLayerParams] as HTMLCanvasElement,
312
+ // context: Object.assign({}, context) as ChartContext<ExtendContext>,
313
+ context: pluginSetupProps.context,
314
+ pluginParams$,
315
+ initLayerParams: patchPluginParams$.getValue()?.[layer._name as keyof AllLayerParams] as unknown || {}
316
+ }
317
+
318
+ layer._enable(layerEnableProps)
319
+ } else {
320
+ layer._disable()
321
+ }
322
+ })
323
+ })
324
+
325
+ return {
326
+ // name: `${config.name}-${Math.random().toString(36).substr(2, 9)}`,
327
+ _name: config.name,
328
+ _elementType: elementType,
329
+ // info: {
330
+ // name: config.name,
331
+ // layers: [config.name]
332
+ // } as PluginInfo,
333
+ // layerIndex: config.layerIndex,
334
+ // contextExtension,
335
+ // layer visibility controls
336
+ _getId: () => _id,
337
+ _setId: (id: string) => {
338
+ _id = id
339
+ },
340
+ // 由 chart 注入 context
341
+ _injectContext: (context) => {
342
+
343
+ context$.next(Object.assign({}, context) as ChartContext<ExtendContext>)
344
+
345
+ // context.size$.subscribe(size => {
346
+ // const svgGElement: SVGGElement | null = context.root.querySelector(`.${svgGClassName}`)
347
+ // const canvasElement: HTMLCanvasElement | null = context.root.querySelector(`.${canvasClassName}`)
348
+ // if (svgGElement) {
349
+ // svgGElement.setAttribute('width', size.width.toString())
350
+ // svgGElement.setAttribute('height', size.height.toString())
351
+ // }
352
+ // if (canvasElement) {
353
+ // canvasElement.width = size.width
354
+ // canvasElement.height = size.height
355
+ // }
356
+ // })
357
+
358
+ // if (config.setup) {
359
+ // // const extension: ExtendContext = config.setup(context)
360
+ // // Object.assign(context, extension)
361
+ // config.setup({
362
+ // context: context as ChartContext<ExtendContext>,
363
+ // svg: mainSvgElement!,
364
+ // canvas: mainCanvasElement!,
365
+ // pluginParams$
366
+ // })
367
+ // }
368
+
369
+ // 顯示全部 layers
370
+ // IsShowLayerNameSet$.next(getAllLayerNamesSet())
371
+ },
372
+ show: (names: (keyof AllLayerParams) | (keyof AllLayerParams)[]) => {
373
+ names = Array.isArray(names) ? names : [names]
374
+ ShownLayerNameSet$.next(new Set([...Array.from(ShownLayerNameSet$.getValue()), ...names]))
375
+ },
376
+ showOnly: (names: (keyof AllLayerParams) | (keyof AllLayerParams)[]) => {
377
+ names = Array.isArray(names) ? names : [names]
378
+ ShownLayerNameSet$.next(new Set([...names]))
379
+ },
380
+ showAll: () => {
381
+ ShownLayerNameSet$.next(getAllLayerNamesSet())
382
+ },
383
+ hide: (names: (keyof AllLayerParams) | (keyof AllLayerParams)[]) => {
384
+ names = Array.isArray(names) ? names : [names]
385
+ ShownLayerNameSet$.next(new Set([...Array.from(ShownLayerNameSet$.getValue()).filter(name => !names.includes(name))]))
386
+ },
387
+ hideAll: () => {
388
+ ShownLayerNameSet$.next(new Set())
389
+ },
390
+ toggle: (names: (keyof AllLayerParams) | (keyof AllLayerParams)[]) => {
391
+ names = Array.isArray(names) ? names : [names]
392
+ Array.from(ShownLayerNameSet$.getValue()).forEach(shown => {
393
+ if (names.includes(shown)) {
394
+ names.splice(names.indexOf(shown), 1)
395
+ } else {
396
+ names.push(shown)
397
+ }
398
+ })
399
+ ShownLayerNameSet$.next(new Set(names))
400
+ },
401
+ getShownLayerNames: () => {
402
+ // const ShownLayerNameSet = getShownLayerNames({
403
+ // InitLayerNameSet: InitLayerNameSet$.getValue(),
404
+ // IsShowLayerNameSet: IsShowLayerNameSet$.getValue()
405
+ // })
406
+ return Array.from(ShownLayerNameSet$.getValue())
407
+ },
408
+ // layer params
409
+ // setLayers: (partial: DeepPartial<PluginParams>) => {
410
+ // params = { ...params, ...partial }
411
+ // },
412
+ updateParams: (patch: DeepPartial<PluginParams | AllLayerParams>) => {
413
+ // plugin params(累積合併至目前值,使連續多次 updateParams 不互相覆蓋)
414
+ currentPluginParams = deepOverwrite(currentPluginParams, patch as DeepPartial<PluginParams>)
415
+ patchPluginParams$.next(patch)
416
+ // layer params
417
+ Object.keys(patch).forEach((key) => {
418
+ const layer = allLayerInstances[key as keyof AllLayerParams]
419
+ if (layer) {
420
+ layer._updateParams((patch as Record<string, any>)[key])
421
+ }
422
+ })
423
+
424
+ // InitLayerNameSet$.next(getParamsKeySet(patch))
425
+ },
426
+ forceReplaceParams: (full: PluginParams | AllLayerParams) => {
427
+ // plugin params
428
+ currentPluginParams = full as PluginParams
429
+ forceReplacePluginParams$.next(full)
430
+ // layer params
431
+ Object.keys(full).forEach((key) => {
432
+ const layer = allLayerInstances[key as keyof AllLayerParams]
433
+ if (layer) {
434
+ layer._forceReplaceParams((full as Record<string, any>)[key])
435
+ }
436
+ })
437
+ // InitLayerNameSet$.next(getParamsKeySet(full))
438
+ },
439
+ getParams: () => {
440
+ // plugin 本身的 params 在前、各 layer params 在後
441
+ const result: Record<string, any> = Object.assign({}, currentPluginParams)
442
+ layers.forEach((layer) => {
443
+ result[layer._name] = layer._getParams()
444
+ })
445
+ return result as PluginParams | AllLayerParams
446
+ },
447
+ // layer: <LayerName extends keyof PluginParams>(name: LayerName) => ({
448
+ // // set: (partial: DeepPartial<PluginParams[LayerName]>) => {
449
+ // // if (params[name]) {
450
+ // // params[name] = { ...params[name], ...partial }
451
+ // // }
452
+ // // },
453
+ // update: (patch: DeepPartial<PluginParams[LayerName]>) => {
454
+ // if (params[name]) {
455
+ // params[name] = { ...params[name], ...patch }
456
+ // }
457
+ // },
458
+ // replace: (full: PluginParams[LayerName]) => {
459
+ // params[name] = full
460
+ // },
461
+ // show: () => {
462
+ // // implementation for showing this layer
463
+ // },
464
+ // hide: () => {
465
+ // // implementation for hiding this layer
466
+ // },
467
+ // toggle: () => {
468
+ // // implementation for toggling this layer
469
+ // }
470
+ // }),
471
+ destroy: () => {
472
+ destroySetup()
473
+ // subscription.unsubscribe()
474
+ context$.complete()
475
+ // IsShowLayerNameSet$.complete()
476
+ ShownLayerNameSet$.complete()
477
+
478
+ // layers 為本實例專屬,destroy 不影響其他 plugin 實例
479
+ layers.forEach((layer) => {
480
+ layer._destroy()
481
+ })
482
+ },
483
+ // // outputs
484
+ // layers$: new Observable<Record<string, PluginParams>>(subscriber => {
485
+ // subscriber.next(params)
486
+ // })
487
+ }
488
+ }