@orbcharts/core 3.0.6 → 4.0.0-pre-alpha.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 +2795 -6591
  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 +59 -46
  47. package/src/OrbCharts.ts +35 -0
  48. package/src/chart/createChart.ts +997 -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 +100 -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 +470 -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 +98 -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 -308
  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,164 @@
1
+ /**
2
+ * DOM 元素生命週期管理工具
3
+ * 提供類似 D3.js 的 enter/update/exit 模式
4
+ */
5
+
6
+ /**
7
+ * 比較兩個數組是否相等
8
+ */
9
+ export function arraysEqual<T>(a: T[], b: T[]): boolean {
10
+ if (a.length !== b.length) return false
11
+ return a.every((val, i) => val === b[i])
12
+ }
13
+
14
+ /**
15
+ * 高效的元素重新排序函數,使用最少的 DOM 操作
16
+ */
17
+ export function reorderElements<T extends Element>(
18
+ parent: Element,
19
+ targetOrder: string[],
20
+ elementsRef: Record<string, T>
21
+ ): void {
22
+ // 獲取當前所有相關的子元素
23
+ const currentElements = targetOrder
24
+ .map(name => elementsRef[name])
25
+ .filter(el => el && el.parentNode === parent)
26
+
27
+ if (currentElements.length === 0) return
28
+
29
+ // 檢查當前順序是否已經正確
30
+ let isCorrectOrder = true
31
+ let previousElement: Element | null = null
32
+
33
+ for (const element of currentElements) {
34
+ if (previousElement && previousElement.nextElementSibling !== element) {
35
+ isCorrectOrder = false
36
+ break
37
+ }
38
+ previousElement = element
39
+ }
40
+
41
+ // 如果順序已經正確,直接返回
42
+ if (isCorrectOrder) return
43
+
44
+ // 使用 DocumentFragment 來批量操作,減少 reflow
45
+ const fragment = document.createDocumentFragment()
46
+
47
+ // 按照目標順序將元素添加到 fragment
48
+ targetOrder.forEach(name => {
49
+ const element = elementsRef[name]
50
+ if (element && element.parentNode === parent) {
51
+ fragment.appendChild(element)
52
+ }
53
+ })
54
+
55
+ // 一次性將所有元素插入回父元素
56
+ parent.appendChild(fragment)
57
+ }
58
+
59
+ /**
60
+ * 元素工廠函數類型
61
+ */
62
+ export type ElementFactory<T extends Element> = (id: string) => T
63
+
64
+ /**
65
+ * 生命週期鉤子函數類型
66
+ */
67
+ export interface LifecycleHooks<T extends Element> {
68
+ /** 元素進入時的回調 */
69
+ onEnter?: (element: T, id: string) => void
70
+ /** 元素更新時的回調 */
71
+ onUpdate?: (element: T, id: string) => void
72
+ /** 元素退出前的回調 */
73
+ onExit?: (element: T, id: string) => void
74
+ }
75
+
76
+ /**
77
+ * D3.js 風格的元素生命週期管理:處理 enter/update/exit
78
+ *
79
+ * @param parent 父容器元素
80
+ * @param targetIds 目標元素 ID 列表(按順序)
81
+ * @param elementsRef 元素引用字典
82
+ * @param createElement 元素創建工廠函數
83
+ * @param hooks 生命週期鉤子函數(可選)
84
+ */
85
+ export function handleElementLifecycle<T extends Element>(
86
+ parent: Element,
87
+ targetIds: string[],
88
+ elementsRef: Record<string, T>,
89
+ createElement: ElementFactory<T>,
90
+ hooks?: LifecycleHooks<T>
91
+ ): void {
92
+ // Exit: 移除不再需要的元素
93
+ const currentIds = Object.keys(elementsRef)
94
+ const toRemove = currentIds.filter(id => !targetIds.includes(id))
95
+
96
+ toRemove.forEach(id => {
97
+ const element = elementsRef[id]
98
+ if (element && element.parentNode === parent) {
99
+ // 執行退出回調
100
+ hooks?.onExit?.(element, id)
101
+
102
+ // 從 DOM 移除
103
+ parent.removeChild(element)
104
+ }
105
+ delete elementsRef[id]
106
+ })
107
+
108
+ // Enter: 創建新元素
109
+ const toAdd = targetIds.filter(id => !elementsRef[id])
110
+
111
+ toAdd.forEach(id => {
112
+ const element = createElement(id)
113
+ elementsRef[id] = element
114
+ parent.appendChild(element)
115
+
116
+ // 執行進入回調
117
+ hooks?.onEnter?.(element, id)
118
+ })
119
+
120
+ // Update: 處理已存在的元素
121
+ const existing = targetIds.filter(id => elementsRef[id] && !toAdd.includes(id))
122
+ existing.forEach(id => {
123
+ const element = elementsRef[id]
124
+ hooks?.onUpdate?.(element, id)
125
+ })
126
+
127
+ // 重新排序所有存在的元素
128
+ if (targetIds.length > 0) {
129
+ reorderElements(parent, targetIds, elementsRef)
130
+ }
131
+ }
132
+
133
+ // /**
134
+ // * 創建 SVG group 元素的工廠函數
135
+ // */
136
+ // export function createSVGGroup(className?: string): ElementFactory<SVGGElement> {
137
+ // return (id: string) => {
138
+ // const g = document.createElementNS('http://www.w3.org/2000/svg', 'g')
139
+ // g.classList.add(className ? `${className}--${id}` : `layer--${id}`)
140
+ // return g
141
+ // }
142
+ // }
143
+
144
+ // /**
145
+ // * 創建 Canvas 元素的工廠函數
146
+ // */
147
+ // export function createCanvas(className?: string): ElementFactory<HTMLCanvasElement> {
148
+ // return (id: string) => {
149
+ // const canvas = document.createElement('canvas')
150
+ // canvas.classList.add(className ? `${className}--${id}` : `layer--${id}`)
151
+ // return canvas
152
+ // }
153
+ // }
154
+
155
+ // /**
156
+ // * 創建通用 DIV 元素的工廠函數
157
+ // */
158
+ // export function createDiv(className?: string): ElementFactory<HTMLDivElement> {
159
+ // return (id: string) => {
160
+ // const div = document.createElement('div')
161
+ // div.classList.add(className ? `${className}--${id}` : `layer--${id}`)
162
+ // return div
163
+ // }
164
+ // }
@@ -0,0 +1,55 @@
1
+ import { isDom } from "./commonUtils";
2
+
3
+ export function createSVG (className?: string): SVGSVGElement {
4
+ const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg')
5
+ svg.setAttribute('xmlns:xlink', 'http://www.w3.org/1999/xlink')
6
+ svg.setAttribute('xmls', 'http://www.w3.org/2000/svg')
7
+ svg.setAttribute('version', '1.1')
8
+ svg.style.position = 'absolute'
9
+ if (className) {
10
+ svg.classList.add(className)
11
+ }
12
+ return svg
13
+ }
14
+
15
+ export function createCanvasElement (className?: string): HTMLCanvasElement {
16
+ const canvas = document.createElement('canvas')
17
+ canvas.style.position = 'absolute'
18
+ if (className) {
19
+ canvas.classList.add(className)
20
+ }
21
+ return canvas
22
+ }
23
+
24
+ export function createSVGGroup(className?: string): SVGGElement {
25
+ const g = document.createElementNS('http://www.w3.org/2000/svg', 'g')
26
+ if (className) {
27
+ g.classList.add(className)
28
+ }
29
+ return g
30
+ }
31
+
32
+ export function createCanvas(className?: string): HTMLCanvasElement {
33
+ const canvas = document.createElement('canvas')
34
+ if (className) {
35
+ canvas.classList.add(className)
36
+ }
37
+ return canvas
38
+ }
39
+
40
+ export function createDiv(className?: string): HTMLDivElement {
41
+ const div = document.createElement('div')
42
+ if (className) {
43
+ div.classList.add(className)
44
+ }
45
+ return div
46
+ }
47
+
48
+ // 清空 element 底下所有元素
49
+ export function removeElementChildren(el: HTMLElement | Element) {
50
+ if (isDom(el)) {
51
+ while (el.firstChild) {
52
+ el.removeChild(el.firstChild)
53
+ }
54
+ }
55
+ }
@@ -1,41 +1,41 @@
1
- // message的prefix - error: 有中斷,warning: 無中斷
2
- export function createMessagePrefix (status: 'warning' | 'error'): string {
3
- return `[OrbCharts ${status}]:`
4
- }
5
-
6
- // throw到最外層的錯誤訊息
7
- export function createOrbChartsErrorMessage (e: Error): string {
8
- return `${createMessagePrefix('error')} ${e.message}`
9
- }
10
-
11
-
12
- // 未預期的錯誤
13
- export function createUnexpectedErrorMessage ({ from, systemMessage }: {
14
- from: string //
15
- systemMessage: string // catch 給的的原生錯誤訊息
16
- }): string {
17
- return `unexpected error from '${from}':
18
- ${systemMessage}`
19
- }
20
-
21
- // validator 的 error 訊息
22
- export function createValidatorErrorMessage ({ columnName, expectToBe, from }: {
23
- columnName: string // e.g. 'seriesLabels'
24
- expectToBe: string // e.g. 'string[]'
25
- from: string // e.g. Chart.chartParams$, Pie.params$
26
- }): string {
27
- return `Invalid value: '${columnName}' must be '${expectToBe}'
28
-
29
- ----> find in '${from}'`
30
- }
31
-
32
- // validator 的 warning 訊息
33
- export function createValidatorWarningMessage ({ columnName, expectToBe, from }: {
34
- columnName: string // e.g. 'seriesLabels'
35
- expectToBe: string // e.g. 'string[]'
36
- from: string // e.g. Chart.chartParams$, Pie.params$
37
- }): string {
38
- return `${createMessagePrefix('warning')} Value is not correct: '${columnName}' suppose to be '${expectToBe}', it may cause unexpected errors.'
39
-
40
- ----> find in '${from}'`
1
+ // message的prefix - error: 有中斷,warning: 無中斷
2
+ export function createMessagePrefix (status: 'warning' | 'error'): string {
3
+ return `[OrbCharts ${status}]:`
4
+ }
5
+
6
+ // throw到最外層的錯誤訊息
7
+ export function createOrbChartsErrorMessage (e: Error): string {
8
+ return `${createMessagePrefix('error')} ${e.message}`
9
+ }
10
+
11
+
12
+ // 未預期的錯誤
13
+ export function createUnexpectedErrorMessage ({ from, systemMessage }: {
14
+ from: string //
15
+ systemMessage: string // catch 給的的原生錯誤訊息
16
+ }): string {
17
+ return `unexpected error from '${from}':
18
+ ${systemMessage}`
19
+ }
20
+
21
+ // validator 的 error 訊息
22
+ export function createValidatorErrorMessage ({ columnName, expectToBe, from }: {
23
+ columnName: string // e.g. 'seriesLabels'
24
+ expectToBe: string // e.g. 'string[]'
25
+ from: string // e.g. Chart.chartParams$, Pie.params$
26
+ }): string {
27
+ return `Invalid value: '${columnName}' must be '${expectToBe}'
28
+
29
+ ----> find in '${from}'`
30
+ }
31
+
32
+ // validator 的 warning 訊息
33
+ export function createValidatorWarningMessage ({ columnName, expectToBe, from }: {
34
+ columnName: string // e.g. 'seriesLabels'
35
+ expectToBe: string // e.g. 'string[]'
36
+ from: string // e.g. Chart.chartParams$, Pie.params$
37
+ }): string {
38
+ return `${createMessagePrefix('warning')} Value is not correct: '${columnName}' suppose to be '${expectToBe}', it may cause unexpected errors.'
39
+
40
+ ----> find in '${from}'`
41
41
  }
@@ -1,4 +1,8 @@
1
- export * from './commonUtils'
2
- export * from './d3Scale'
3
- export * from './observables'
4
- export * from './orbchartsUtils'
1
+ export * from './aggregateUtils'
2
+ export * from './colorUtils'
3
+ export * from './commonUtils'
4
+ export * from './dom'
5
+ export * from './errorMessage'
6
+ export * from './observables'
7
+ export * from './orbchartsUtils'
8
+ export * from './validator'
@@ -1,309 +1,17 @@
1
- import {
2
- combineLatest,
3
- distinctUntilChanged,
4
- filter,
5
- map,
6
- merge,
7
- takeUntil,
8
- shareReplay,
9
- switchMap,
10
- Subject,
11
- Observable } from 'rxjs'
12
- import type {
13
- ChartType,
14
- ChartParams,
15
- ComputedDatumBase,
16
- ComputedDataTypeMap,
17
- ComputedDatumTypeMap,
18
- ContainerPositionScaled,
19
- DataFormatterContainer,
20
- DataFormatterTypeMap,
21
- EventTypeMap,
22
- HighlightTarget,
23
- Layout,
24
- TransformData } from '../../lib/core-types'
25
-
26
- // interface DatumUnknown {
27
- // value: number | null
28
- // id: string
29
- // // label: string
30
- // seriesLabel?: string // 要符合每一種computedData所以不一定會有seriesLabel
31
- // groupLabel?: string // 要符合每一種computedData所以不一定會有groupLabel
32
- // }
33
-
34
- export function resizeObservable(elem: HTMLElement | Element): Observable<DOMRectReadOnly> {
35
- return new Observable(subscriber => {
36
- const ro = new ResizeObserver(entries => {
37
- const entry = entries[0]
38
- if (entry && entry.contentRect) {
39
- subscriber.next(entry.contentRect)
40
- }
41
- })
42
-
43
- ro.observe(elem)
44
- return function unsubscribe() {
45
- ro.unobserve(elem)
46
- }
47
- })
48
- }
49
-
50
- interface HighlightTargetValue {
51
- id: string | null
52
- label: string | null
53
- seriesLabel: string | null
54
- groupLabel: string | null
55
- categoryLabel: string | null
56
- highlightDefault: string | null
57
- }
58
-
59
- // 通用 highlight Observable
60
- export const highlightObservable = <T extends ChartType, D>({ datumList$, fullChartParams$, event$ }: {
61
- datumList$: Observable<D[]>
62
- fullChartParams$: Observable<ChartParams>
63
- event$: Subject<EventTypeMap<T>>
64
- }): Observable<D[]> => {
65
- const destroy$ = new Subject()
66
-
67
- // 預設的highlight
68
- const highlightDefault$: Observable<HighlightTargetValue> = fullChartParams$.pipe(
69
- takeUntil(destroy$),
70
- map(d => d.highlightDefault),
71
- distinctUntilChanged(),
72
- map(highlightDefault => {
73
- return {
74
- id: null,
75
- label: null,
76
- seriesLabel: null,
77
- groupLabel: null,
78
- categoryLabel: null,
79
- highlightDefault
80
- } as HighlightTargetValue
81
- }),
82
- shareReplay(1)
83
- )
84
-
85
- const highlightTarget$: Observable<HighlightTarget> = fullChartParams$.pipe(
86
- takeUntil(destroy$),
87
- map(d => d.highlightTarget),
88
- distinctUntilChanged(),
89
- shareReplay(1)
90
- )
91
-
92
- // 事件觸發的highlight
93
- const highlightMouseover$: Observable<HighlightTargetValue> = highlightTarget$.pipe(
94
- switchMap(highlightTarget => {
95
- return event$.pipe(
96
- takeUntil(destroy$),
97
- // filter(d => d.eventName === 'mouseover' || d.eventName === 'mousemove'),
98
- filter(d => d.eventName === 'mouseover'),
99
- // distinctUntilChanged((prev, current) => prev.eventName === current.eventName)
100
- map(_d => {
101
- const d = _d as any
102
- return d.datum
103
- ? {
104
- id: d.datum.id,
105
- label: null, // label有可能重覆所以不做判斷
106
- seriesLabel: highlightTarget === 'series' ? d.datum.seriesLabel : null,
107
- groupLabel: highlightTarget === 'group' ? d.datum.groupLabel : null,
108
- categoryLabel: highlightTarget === 'category' ? d.datum.categoryLabel : null,
109
- highlightDefault: null
110
- } as HighlightTargetValue
111
- : {
112
- id: null,
113
- label: null,
114
- seriesLabel: null,
115
- groupLabel: null,
116
- categoryLabel: null,
117
- highlightDefault: null
118
- } as HighlightTargetValue
119
- })
120
- )
121
- })
122
- )
123
-
124
- const highlightMouseout$ = event$.pipe(
125
- takeUntil(destroy$),
126
- filter(d => d.eventName === 'mouseout'),
127
- // distinctUntilChanged((prev, current) => prev.eventName === current.eventName)
128
- // map(d => {
129
- // return { id: '', label: '' }
130
- // })
131
- switchMap(d => highlightDefault$)
132
- )
133
-
134
- // function getDatumIds (datumList: ComputedDatumTypeMap<T>[], id: string | null) {
135
- // const datum = datumList.find(d => (d as ComputedDatumBase).id === id)
136
- // return datum ? [datum] : []
137
- // }
138
- function getDatumIds (datumList: ComputedDatumTypeMap<T>[], id: string | null, label: string | null) {
139
- return id == null && label == null
140
- ? []
141
- : datumList.filter(d => (d as ComputedDatumBase).id === id || (d as ComputedDatumBase).label === label)
142
- }
143
-
144
- function getSeriesIds (datumList: ComputedDatumTypeMap<T>[], seriesLabel: string | null) {
145
- return seriesLabel == null
146
- ? []
147
- : datumList.filter(d => (d as ComputedDatumTypeMap<"series">).seriesLabel === seriesLabel)
148
- }
149
-
150
- function getGroupIds (datumList: ComputedDatumTypeMap<T>[], groupLabel: string | null) {
151
- return groupLabel == null
152
- ? []
153
- : datumList.filter(d => (d as ComputedDatumTypeMap<"grid">).groupLabel === groupLabel)
154
- }
155
-
156
- function getCategoryIds (datumList: ComputedDatumTypeMap<T>[], categoryLabel: string | null) {
157
- return categoryLabel == null
158
- ? []
159
- : datumList.filter(d => (d as ComputedDatumTypeMap<"multiValue" | "relationship" | "tree">).categoryLabel === categoryLabel)
160
- }
161
-
162
- return new Observable<D[]>(subscriber => {
163
- combineLatest({
164
- target: merge(highlightMouseover$, highlightMouseout$, highlightDefault$),
165
- datumList: datumList$,
166
- fullChartParams: fullChartParams$,
167
- }).pipe(
168
- takeUntil(destroy$),
169
- switchMap(async d => d)
170
- ).subscribe(data => {
171
- let datumList: ComputedDatumTypeMap<T>[] = []
172
- if (data.fullChartParams.highlightTarget === 'datum') {
173
- datumList = getDatumIds(data.datumList as ComputedDatumTypeMap<T>[], data.target.id, data.target.label)
174
- } else if (data.fullChartParams.highlightTarget === 'series') {
175
- datumList = getSeriesIds(data.datumList as ComputedDatumTypeMap<T>[], data.target.seriesLabel)
176
- } else if (data.fullChartParams.highlightTarget === 'group') {
177
- datumList = getGroupIds(data.datumList as ComputedDatumTypeMap<T>[], data.target.groupLabel)
178
- } else if (data.fullChartParams.highlightTarget === 'category') {
179
- datumList = getCategoryIds(data.datumList as ComputedDatumTypeMap<T>[], data.target.categoryLabel)
180
- }
181
-
182
- subscriber.next(datumList as D[])
183
- })
184
-
185
- return function unsubscribe () {
186
- destroy$.next(undefined)
187
- }
188
- })
189
- }
190
-
191
- export const seriesDataMapObservable = <DatumType extends ComputedDatumTypeMap<'series' | 'grid'>>({ datumList$ }: { datumList$: Observable<DatumType[]> }) => {
192
- return datumList$.pipe(
193
- map(data => {
194
- const SeriesDataMap: Map<string, DatumType[]> = new Map()
195
- data.forEach(d => {
196
- const seriesData = SeriesDataMap.get(d.seriesLabel) ?? []
197
- seriesData.push(d)
198
- SeriesDataMap.set(d.seriesLabel, seriesData)
199
- })
200
- return SeriesDataMap
201
- })
202
- )
203
- }
204
-
205
- export const groupDataMapObservable = <DatumType extends ComputedDatumTypeMap<'grid'>> ({ datumList$ }: { datumList$: Observable<DatumType[]> }) => {
206
- return datumList$.pipe(
207
- map(data => {
208
- const GroupDataMap: Map<string, DatumType[]> = new Map()
209
- data.forEach(d => {
210
- const groupData = GroupDataMap.get(d.groupLabel) ?? []
211
- groupData.push(d)
212
- GroupDataMap.set(d.groupLabel, groupData)
213
- })
214
- return GroupDataMap
215
- })
216
- )
217
- }
218
-
219
- export const categoryDataMapObservable = <DatumType extends ComputedDatumTypeMap<'multiValue' | 'relationship' | 'tree'>> ({ datumList$ }: { datumList$: Observable<DatumType[]> }) => {
220
- return datumList$.pipe(
221
- map(data => {
222
- const GroupDataMap: Map<string, DatumType[]> = new Map()
223
- data
224
- .filter(d => d.categoryLabel != null)
225
- .forEach(d => {
226
- const groupData = GroupDataMap.get(d.categoryLabel) ?? []
227
- groupData.push(d)
228
- GroupDataMap.set(d.categoryLabel, groupData)
229
- })
230
- return GroupDataMap
231
- })
232
- )
233
- }
234
-
235
- export const textSizePxObservable = (chartParams$: Observable<ChartParams>) => {
236
- return chartParams$.pipe(
237
- map(d => d.styles.textSize),
238
- distinctUntilChanged(),
239
- map(data => {
240
- let value = NaN
241
- if (typeof data === 'string') {
242
- if (data.includes('rem')) {
243
- const rootFontSizePx = parseFloat(getComputedStyle(document.documentElement).fontSize)
244
- const num = parseFloat(data)
245
- value = num * rootFontSizePx
246
- } else if (data.includes('px')) {
247
- value = parseFloat(data)
248
- }
249
- } else if (typeof data === 'number') {
250
- return data
251
- }
252
- return value ? value : 14 // default
253
- })
254
- )
255
- }
256
-
257
- export const containerSizeObservable = ({ layout$, containerPosition$, container$ }: {
258
- layout$: Observable<Layout>
259
- containerPosition$: Observable<ContainerPositionScaled[]>
260
- container$: Observable<DataFormatterContainer>
261
- }) => {
262
- const rowAmount$ = containerPosition$.pipe(
263
- map(containerPosition => {
264
- const maxRowIndex = containerPosition.reduce((acc, current) => {
265
- return current.rowIndex > acc ? current.rowIndex : acc
266
- }, 0)
267
- return maxRowIndex + 1
268
- }),
269
- distinctUntilChanged(),
270
- )
271
-
272
- const columnAmount$ = containerPosition$.pipe(
273
- map(containerPosition => {
274
- const maxColumnIndex = containerPosition.reduce((acc, current) => {
275
- return current.columnIndex > acc ? current.columnIndex : acc
276
- }, 0)
277
- return maxColumnIndex + 1
278
- }),
279
- distinctUntilChanged()
280
- )
281
-
282
- return combineLatest({
283
- layout: layout$,
284
- rowAmount: rowAmount$,
285
- columnAmount: columnAmount$,
286
- container: container$
287
- }).pipe(
288
- switchMap(async (d) => d),
289
- map(data => {
290
- // const width = (data.layout.rootWidth / data.columnAmount) - (data.layout.left + data.layout.right)
291
- // const height = (data.layout.rootHeight / data.rowAmount) - (data.layout.top + data.layout.bottom)
292
- const columnGap = data.container.columnGap === 'auto'
293
- ? data.layout.left + data.layout.right
294
- : data.container.columnGap
295
- const rowGap = data.container.rowGap === 'auto'
296
- ? data.layout.top + data.layout.bottom
297
- : data.container.rowGap
298
- const width = (data.layout.rootWidth - data.layout.left - data.layout.right - (columnGap * (data.columnAmount - 1))) / data.columnAmount
299
- const height = (data.layout.rootHeight - data.layout.top - data.layout.bottom - (rowGap * (data.rowAmount - 1))) / data.rowAmount
300
-
301
- return {
302
- width,
303
- height
304
- }
305
- }),
306
- distinctUntilChanged((a, b) => a.width === b.width && a.height === b.height),
307
- // shareReplay(1)
308
- )
1
+ import { Observable, BehaviorSubject } from 'rxjs'
2
+
3
+ export function resizeObservable(elem: HTMLElement | Element): Observable<DOMRectReadOnly> {
4
+ return new Observable(subscriber => {
5
+ const ro = new ResizeObserver(entries => {
6
+ const entry = entries[0]
7
+ if (entry && entry.contentRect) {
8
+ subscriber.next(entry.contentRect)
9
+ }
10
+ })
11
+
12
+ ro.observe(elem)
13
+ return function unsubscribe() {
14
+ ro.unobserve(elem)
15
+ }
16
+ })
309
17
  }