@orbcharts/core 3.0.7 → 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 (196) hide show
  1. package/dist/orbcharts-core.es.js +2795 -6591
  2. package/dist/orbcharts-core.umd.js +6 -6
  3. package/dist/src/OrbCharts.d.ts +18 -0
  4. package/dist/src/chart/createChart.d.ts +3 -0
  5. package/dist/src/chart/createGraphData.d.ts +3 -0
  6. package/dist/src/chart/createGridData.d.ts +3 -0
  7. package/dist/src/chart/createMultivariateData.d.ts +3 -0
  8. package/dist/src/chart/createSeriesData.d.ts +3 -0
  9. package/dist/src/chart/createTreeData.d.ts +3 -0
  10. package/dist/src/chart/defaults.d.ts +5 -0
  11. package/dist/src/defineCanvasLayer.d.ts +16 -0
  12. package/dist/src/defineCanvasPlugin.d.ts +22 -0
  13. package/dist/src/defineSVGLayer.d.ts +16 -0
  14. package/dist/src/defineSVGPlugin.d.ts +22 -0
  15. package/dist/src/index.d.ts +6 -14
  16. package/dist/src/layer/createLayer.d.ts +3 -0
  17. package/dist/src/plugin/createPlugin.d.ts +3 -0
  18. package/dist/src/test/createGraphData.test.d.ts +1 -0
  19. package/dist/src/test/createTreeData.test.d.ts +1 -0
  20. package/dist/src/test/simple-graph-test.d.ts +74 -0
  21. package/dist/src/test/simple-tree-test.d.ts +13 -0
  22. package/dist/src/types/Chart.d.ts +39 -0
  23. package/dist/src/types/ChartContext.d.ts +27 -0
  24. package/dist/src/types/Common.d.ts +3 -0
  25. package/dist/src/types/Encoding.d.ts +33 -0
  26. package/dist/src/types/Event.d.ts +12 -0
  27. package/dist/src/types/Layers.d.ts +55 -0
  28. package/dist/src/types/ModelData.d.ts +70 -0
  29. package/dist/src/types/Plugin.d.ts +39 -0
  30. package/dist/src/types/RawData.d.ts +18 -0
  31. package/dist/src/types/RenderData.d.ts +4 -0
  32. package/dist/src/types/Theme.d.ts +17 -0
  33. package/dist/src/types/Validator.d.ts +20 -0
  34. package/dist/src/types/index.d.ts +12 -0
  35. package/dist/src/utils/aggregateUtils.d.ts +37 -0
  36. package/dist/src/utils/colorUtils.d.ts +22 -0
  37. package/dist/src/utils/commonUtils.d.ts +3 -5
  38. package/dist/src/utils/dom-lifecycle.d.ts +37 -0
  39. package/dist/src/utils/dom.d.ts +6 -0
  40. package/dist/src/utils/index.d.ts +5 -1
  41. package/dist/src/utils/observables.d.ts +1 -25
  42. package/dist/src/utils/orbchartsUtils.d.ts +2 -53
  43. package/dist/src/utils/validator.d.ts +2 -2
  44. package/dist/test/aggregateTest.d.ts +1 -0
  45. package/package.json +21 -8
  46. package/src/OrbCharts.ts +35 -0
  47. package/src/chart/createChart.ts +997 -0
  48. package/src/chart/createGraphData.ts +391 -0
  49. package/src/chart/createGridData.ts +247 -0
  50. package/src/chart/createMultivariateData.ts +181 -0
  51. package/src/chart/createSeriesData.ts +297 -0
  52. package/src/chart/createTreeData.ts +344 -0
  53. package/src/chart/defaults.ts +100 -0
  54. package/src/defineCanvasLayer.ts +24 -0
  55. package/src/defineCanvasPlugin.ts +39 -0
  56. package/src/defineSVGLayer.ts +24 -0
  57. package/src/defineSVGPlugin.ts +39 -0
  58. package/src/index.ts +6 -18
  59. package/src/layer/createLayer.ts +138 -0
  60. package/src/plugin/createPlugin.ts +470 -0
  61. package/src/test/createGraphData.test.ts +103 -0
  62. package/src/test/createTreeData.test.ts +97 -0
  63. package/src/test/simple-graph-test.js +51 -0
  64. package/src/test/simple-tree-test.js +58 -0
  65. package/src/types/Chart.ts +62 -0
  66. package/src/types/ChartContext.ts +42 -0
  67. package/src/types/Common.ts +5 -0
  68. package/src/types/Encoding.ts +43 -0
  69. package/src/types/Event.ts +26 -0
  70. package/src/types/Layers.ts +93 -0
  71. package/src/types/ModelData.ts +95 -0
  72. package/src/types/Plugin.ts +98 -0
  73. package/src/types/RawData.ts +67 -0
  74. package/src/types/RenderData.ts +16 -0
  75. package/src/types/Theme.ts +21 -0
  76. package/src/types/Validator.ts +36 -0
  77. package/src/types/index.ts +12 -0
  78. package/src/utils/aggregateUtils.ts +99 -0
  79. package/src/utils/colorUtils.ts +63 -0
  80. package/src/utils/commonUtils.ts +12 -11
  81. package/src/utils/dom-lifecycle.ts +164 -0
  82. package/src/utils/dom.ts +55 -0
  83. package/src/utils/index.ts +6 -2
  84. package/src/utils/observables.ts +1 -292
  85. package/src/utils/orbchartsUtils.ts +6 -393
  86. package/src/utils/validator.ts +15 -14
  87. package/dist/lib/core-types.d.ts +0 -1
  88. package/dist/src/AbstractChart.d.ts +0 -19
  89. package/dist/src/GridChart.d.ts +0 -6
  90. package/dist/src/MultiGridChart.d.ts +0 -6
  91. package/dist/src/MultiValueChart.d.ts +0 -6
  92. package/dist/src/RelationshipChart.d.ts +0 -6
  93. package/dist/src/SeriesChart.d.ts +0 -6
  94. package/dist/src/TreeChart.d.ts +0 -6
  95. package/dist/src/base/createBaseChart.d.ts +0 -3
  96. package/dist/src/base/createBasePlugin.d.ts +0 -3
  97. package/dist/src/base/validators/chartOptionsValidator.d.ts +0 -3
  98. package/dist/src/base/validators/chartParamsValidator.d.ts +0 -3
  99. package/dist/src/base/validators/elementValidator.d.ts +0 -3
  100. package/dist/src/base/validators/pluginsValidator.d.ts +0 -3
  101. package/dist/src/defaults.d.ts +0 -25
  102. package/dist/src/defineGridPlugin.d.ts +0 -1
  103. package/dist/src/defineMultiGridPlugin.d.ts +0 -1
  104. package/dist/src/defineMultiValuePlugin.d.ts +0 -1
  105. package/dist/src/defineNoneDataPlugin.d.ts +0 -1
  106. package/dist/src/defineRelationshipPlugin.d.ts +0 -1
  107. package/dist/src/defineSeriesPlugin.d.ts +0 -1
  108. package/dist/src/defineTreePlugin.d.ts +0 -1
  109. package/dist/src/grid/computedDataFn.d.ts +0 -4
  110. package/dist/src/grid/contextObserverCallback.d.ts +0 -3
  111. package/dist/src/grid/dataFormatterValidator.d.ts +0 -3
  112. package/dist/src/grid/dataValidator.d.ts +0 -3
  113. package/dist/src/grid/gridObservables.d.ts +0 -64
  114. package/dist/src/multiGrid/computedDataFn.d.ts +0 -3
  115. package/dist/src/multiGrid/contextObserverCallback.d.ts +0 -3
  116. package/dist/src/multiGrid/dataFormatterValidator.d.ts +0 -3
  117. package/dist/src/multiGrid/dataValidator.d.ts +0 -3
  118. package/dist/src/multiGrid/multiGridObservables.d.ts +0 -16
  119. package/dist/src/multiValue/computedDataFn.d.ts +0 -3
  120. package/dist/src/multiValue/contextObserverCallback.d.ts +0 -3
  121. package/dist/src/multiValue/dataFormatterValidator.d.ts +0 -3
  122. package/dist/src/multiValue/dataValidator.d.ts +0 -3
  123. package/dist/src/multiValue/multiValueObservables.d.ts +0 -130
  124. package/dist/src/relationship/computedDataFn.d.ts +0 -3
  125. package/dist/src/relationship/contextObserverCallback.d.ts +0 -3
  126. package/dist/src/relationship/dataFormatterValidator.d.ts +0 -3
  127. package/dist/src/relationship/dataValidator.d.ts +0 -3
  128. package/dist/src/relationship/relationshipObservables.d.ts +0 -13
  129. package/dist/src/series/computedDataFn.d.ts +0 -3
  130. package/dist/src/series/contextObserverCallback.d.ts +0 -3
  131. package/dist/src/series/dataFormatterValidator.d.ts +0 -3
  132. package/dist/src/series/dataValidator.d.ts +0 -3
  133. package/dist/src/series/seriesObservables.d.ts +0 -37
  134. package/dist/src/tree/computedDataFn.d.ts +0 -3
  135. package/dist/src/tree/contextObserverCallback.d.ts +0 -3
  136. package/dist/src/tree/dataFormatterValidator.d.ts +0 -3
  137. package/dist/src/tree/dataValidator.d.ts +0 -3
  138. package/dist/src/tree/treeObservables.d.ts +0 -10
  139. package/dist/src/utils/d3Scale.d.ts +0 -28
  140. package/lib/core-types.ts +0 -7
  141. package/src/AbstractChart.ts +0 -57
  142. package/src/GridChart.ts +0 -25
  143. package/src/MultiGridChart.ts +0 -25
  144. package/src/MultiValueChart.ts +0 -25
  145. package/src/RelationshipChart.ts +0 -25
  146. package/src/SeriesChart.ts +0 -25
  147. package/src/TreeChart.ts +0 -25
  148. package/src/base/createBaseChart.ts +0 -524
  149. package/src/base/createBasePlugin.ts +0 -154
  150. package/src/base/validators/chartOptionsValidator.ts +0 -24
  151. package/src/base/validators/chartParamsValidator.ts +0 -134
  152. package/src/base/validators/elementValidator.ts +0 -14
  153. package/src/base/validators/pluginsValidator.ts +0 -15
  154. package/src/defaults.ts +0 -284
  155. package/src/defineGridPlugin.ts +0 -3
  156. package/src/defineMultiGridPlugin.ts +0 -3
  157. package/src/defineMultiValuePlugin.ts +0 -3
  158. package/src/defineNoneDataPlugin.ts +0 -4
  159. package/src/defineRelationshipPlugin.ts +0 -3
  160. package/src/defineSeriesPlugin.ts +0 -3
  161. package/src/defineTreePlugin.ts +0 -3
  162. package/src/grid/computedDataFn.ts +0 -129
  163. package/src/grid/contextObserverCallback.ts +0 -209
  164. package/src/grid/dataFormatterValidator.ts +0 -126
  165. package/src/grid/dataValidator.ts +0 -13
  166. package/src/grid/gridObservables.ts +0 -699
  167. package/src/multiGrid/computedDataFn.ts +0 -123
  168. package/src/multiGrid/contextObserverCallback.ts +0 -109
  169. package/src/multiGrid/dataFormatterValidator.ts +0 -121
  170. package/src/multiGrid/dataValidator.ts +0 -13
  171. package/src/multiGrid/multiGridObservables.ts +0 -367
  172. package/src/multiValue/computedDataFn.ts +0 -113
  173. package/src/multiValue/contextObserverCallback.ts +0 -328
  174. package/src/multiValue/dataFormatterValidator.ts +0 -95
  175. package/src/multiValue/dataValidator.ts +0 -13
  176. package/src/multiValue/multiValueObservables.ts +0 -865
  177. package/src/relationship/computedDataFn.ts +0 -159
  178. package/src/relationship/contextObserverCallback.ts +0 -80
  179. package/src/relationship/dataFormatterValidator.ts +0 -14
  180. package/src/relationship/dataValidator.ts +0 -14
  181. package/src/relationship/relationshipObservables.ts +0 -85
  182. package/src/series/computedDataFn.ts +0 -88
  183. package/src/series/contextObserverCallback.ts +0 -132
  184. package/src/series/dataFormatterValidator.ts +0 -47
  185. package/src/series/dataValidator.ts +0 -13
  186. package/src/series/seriesObservables.ts +0 -210
  187. package/src/tree/computedDataFn.ts +0 -129
  188. package/src/tree/contextObserverCallback.ts +0 -58
  189. package/src/tree/dataFormatterValidator.ts +0 -14
  190. package/src/tree/dataValidator.ts +0 -14
  191. package/src/tree/treeObservables.ts +0 -106
  192. package/src/utils/d3Scale.ts +0 -198
  193. package/tsconfig.base.json +0 -14
  194. package/tsconfig.json +0 -3
  195. package/vite-env.d.ts +0 -7
  196. package/vite.config.js +0 -23
@@ -0,0 +1,98 @@
1
+ import { Observable, Subject } from 'rxjs'
2
+ import type {
3
+ DeepPartial,
4
+ LayerEntity,
5
+ ChartContext,
6
+ ExtendableContext
7
+ } from './index'
8
+ import { ValidatorResult } from '../types/Validator'
9
+
10
+
11
+ export interface PluginInfo {
12
+ id: string
13
+ name: string
14
+ elementType: 'svg' | 'canvas'
15
+ shownLayers: string[]
16
+ }
17
+
18
+ // export interface PluginSetupProps<ExtendContext extends ExtendableContext, PluginParams extends Record<string, any>> {
19
+ // context: ChartContext<ExtendContext>
20
+ // svg: SVGElement
21
+ // canvas: HTMLCanvasElement
22
+ // pluginParams$: Observable<PluginParams>
23
+ // }
24
+
25
+ // export interface SVGPluginSetupProps<ExtendContext extends ExtendableContext, PluginParams extends Record<string, any>> {
26
+ // context: ChartContext<ExtendContext>
27
+ // svgG: SVGGElement
28
+ // pluginParams$: Observable<PluginParams>
29
+ // }
30
+
31
+ // export interface CanvasPluginSetupProps<ExtendContext extends ExtendableContext, PluginParams extends Record<string, any>> {
32
+ // context: ChartContext<ExtendContext>
33
+ // canvas: HTMLCanvasElement
34
+ // pluginParams$: Observable<PluginParams>
35
+ // }
36
+
37
+ // export type PluginSetupProps<ElementType extends 'svg' | 'canvas', ExtendContext extends ExtendableContext, PluginParams extends Record<string, any>> =
38
+ // ElementType extends 'svg' ? SVGPluginSetupProps<ExtendContext, PluginParams> :
39
+ // ElementType extends 'canvas' ? CanvasPluginSetupProps<ExtendContext, PluginParams> :
40
+ // never
41
+
42
+ export interface PluginSetupProps<ExtendContext extends ExtendableContext, PluginParams extends Record<string, any>> {
43
+ context: ChartContext<ExtendContext>
44
+ pluginParams$: Observable<PluginParams>
45
+ }
46
+
47
+ export interface DefinePluginConfig<ExtendContext extends ExtendableContext, PluginParams extends Record<string, any>, AllLayerParams extends Record<string, any>>{
48
+ name: string
49
+ defaultParams?: PluginParams
50
+ validator?: (params: DeepPartial<AllLayerParams | PluginParams>) => ValidatorResult
51
+ // { valid: boolean; errors?: string[] }
52
+ layers?: LayerEntity<ExtendContext, PluginParams, AllLayerParams[keyof AllLayerParams]>[]
53
+ // extendContext?: (context: Readonly<ChartContext>) => ExtendContext
54
+ setup?: (props: PluginSetupProps<ExtendContext, PluginParams>) => () => void
55
+ }
56
+
57
+ // export interface CreatePlugin<PluginParams> {
58
+ // (config: DefinePluginConfig<PluginParams>): PluginEntity<PluginParams>
59
+ // }
60
+
61
+
62
+ export interface PluginEntity<ElementType extends 'svg' | 'canvas', PluginParams extends Record<string, any>, AllLayerParams extends Record<string, any>> {
63
+ readonly _name: string
64
+ readonly _elementType: ElementType
65
+ _getId: () => string
66
+ _setId: (id: string) => void
67
+ _injectContext(context: ChartContext<{}>): void
68
+ // layer visibility controls
69
+ show(names: (keyof AllLayerParams) | (keyof AllLayerParams)[]): void
70
+ showOnly(names: (keyof AllLayerParams) | (keyof AllLayerParams)[]): void
71
+ showAll(): void
72
+ hide(names: (keyof AllLayerParams) | (keyof AllLayerParams)[]): void
73
+ hideAll(): void
74
+ toggle(names: (keyof AllLayerParams) | (keyof AllLayerParams)[]): void
75
+ getShownLayerNames(): (keyof AllLayerParams)[]
76
+ // layer params
77
+ // setLayers(partial: DeepPartial<PluginParams>): void // deep-merge with default
78
+ updateParams(patch: DeepPartial<PluginParams | AllLayerParams>): void // deep-merge with previous
79
+ forceReplaceParams(full: PluginParams | AllLayerParams): void // replace(特殊需求,可節省效能)
80
+ getParams(): Readonly<PluginParams | AllLayerParams>
81
+ // layer<LayerName extends keyof PluginParams>(name: LayerName): {
82
+ // // set(partial: DeepPartial<PluginParams[LayerName]>): void // deep-merge with default 該 layer 的 params
83
+ // update(patch: DeepPartial<PluginParams[LayerName]>): void // deep-merge with previous 該 layer 的 params
84
+ // replace(full: PluginParams[LayerName]): void // replace(特殊需求,可節省效能)
85
+ // show(): void
86
+ // hide(): void
87
+ // toggle(): void
88
+ // }
89
+
90
+ destroy(): void
91
+
92
+ // outputs (observables)
93
+ // layers$: Observable<LayersConfig> // 各 layer 的有效參數(合併後)
94
+ // visibleLayerNames$: Observable<string[]> // 目前可見的 layer 清單(原 show$)
95
+ // event$: Observable<{ data: EventData; event: Event }> // 互動事件
96
+ }
97
+
98
+
@@ -0,0 +1,67 @@
1
+
2
+
3
+ // RawData -> ModelData (DataEndoding & ModelType) -> RenderData (Plugin)
4
+
5
+
6
+
7
+ export type RawData = RawDataColumn[] | RawDataColumn[][] // 二維陣列會轉化為多個dataset
8
+
9
+ // 可透過 DataEncoding 更改欄位名稱的預設欄位
10
+ export type DynamicDefaultRawDataFields = {
11
+ value?: number | null
12
+ x?: number | null // multivariate
13
+ y?: number | null // multivariate
14
+ z?: number | null // multivariate
15
+ dataset?: string
16
+ series?: string
17
+ category?: string
18
+ }
19
+
20
+ export type RawDataColumn<DynamicFields extends Record<string, any> = DynamicDefaultRawDataFields> = {
21
+ // 不可變更的基本欄位
22
+ id?: string
23
+ name?: string
24
+ source?: string
25
+ target?: string
26
+ parent?: string
27
+ data?: any
28
+ } & DynamicFields // 可變更的欄位
29
+
30
+
31
+
32
+ // export type DynamicRawDataColumn<
33
+ // ValueField extends string = 'value',
34
+ // DatasetField extends string = 'dataset',
35
+ // SeriesField extends string = 'series',
36
+ // CategoryField extends string = 'category'
37
+ // > = {
38
+ // id?: string
39
+ // name?: string
40
+ // source?: string
41
+ // target?: string
42
+ // parent?: string
43
+ // data?: any
44
+ // } & {
45
+ // [K in ValueField]: number | null
46
+ // } & {
47
+ // [K in DatasetField]?: string
48
+ // } & {
49
+ // [K in SeriesField]?: string
50
+ // } & {
51
+ // [K in CategoryField]?: string
52
+ // }
53
+
54
+ // export type FlexibleRawDataColumn<T extends Record<string, any> = {}> = {
55
+ // id?: string
56
+ // value?: number | null
57
+ // name?: string
58
+ // dataset?: string
59
+ // series?: string
60
+ // category?: string
61
+ // source?: string
62
+ // target?: string
63
+ // parent?: string
64
+ // data?: any
65
+ // } & T
66
+
67
+
@@ -0,0 +1,16 @@
1
+ import type {
2
+ ModelType,
3
+ ModelDatum,
4
+ ModelDatumGraphEdge
5
+ } from './index'
6
+
7
+ // 基礎型別,其他欄位留給 Plugin 擴充
8
+ // export interface RenderDatumBase<T extends ModelType> {
9
+ // modelDatum: ModelDatum<T>
10
+ // }
11
+ export type RenderDatumBase<
12
+ T extends ModelType,
13
+ ExtendTypes extends Record<string, any> = {}
14
+ > = ModelDatum<T> & ExtendTypes
15
+
16
+ export type RenderDatumGraphEdge<ExtendTypes extends Record<string, any> = {}> = ModelDatumGraphEdge & ExtendTypes
@@ -0,0 +1,21 @@
1
+
2
+ export interface Colors {
3
+ data: string[],
4
+ primary: string,
5
+ secondary: string,
6
+ dataContrast: string[],
7
+ background: string
8
+ }
9
+
10
+ export type ColorScheme = 'light' | 'dark'
11
+
12
+ export interface Theme {
13
+ colorScheme: ColorScheme | 'auto'
14
+ colors: {
15
+ light: Colors,
16
+ dark: Colors
17
+ },
18
+ fontSize: string | number
19
+ }
20
+
21
+ export type ColorType = 'none' | keyof Colors
@@ -0,0 +1,36 @@
1
+ export type ToBeTypes = 'string' | 'number' | 'boolean' | 'object' | 'object[]' | 'string[]' | 'number[]' | 'Function' | 'null' | 'undefined'
2
+
3
+ export type ToBeOption = 'ColorType'
4
+
5
+ // 有使用定義好的型別則不需寫 validate
6
+ export interface ValidatorRuleToBeTypes {
7
+ toBeTypes: ToBeTypes[]
8
+ }
9
+
10
+ // 自訂規則
11
+ export interface ValidatorRuleToBe {
12
+ toBe: string
13
+ test: (value: any) => boolean
14
+ }
15
+
16
+ // 選項資料型別
17
+ export interface ValidatorRuleToBeOption {
18
+ toBeOption: ToBeOption
19
+ }
20
+
21
+ export type ValidatorRule<T> = {[key in keyof T]: ValidatorRuleToBeTypes | ValidatorRuleToBe | ValidatorRuleToBeOption}
22
+
23
+
24
+ // export type ValidateObject<T> = (data: T, rules: ValidatorRule<T>) => ValidatorResult
25
+
26
+ // export interface ValidatorUtils {
27
+ // validateObject: typeof validateObject // 我發現要這樣寫才能夠透過 data 型別自動推斷出 T,不曉得有沒有更好的寫法
28
+ // }
29
+
30
+
31
+ export interface ValidatorResult {
32
+ status: 'success' | 'warning' | 'error'
33
+ // message: string // warning or error message
34
+ columnName: string,
35
+ expectToBe: string,
36
+ }
@@ -0,0 +1,12 @@
1
+ export * from './Chart'
2
+ export * from './ChartContext'
3
+ export * from './Encoding'
4
+ export * from './Event'
5
+ export * from './Layers'
6
+ export * from './ModelData'
7
+ export * from './Plugin'
8
+ export * from './RawData'
9
+ export * from './RenderData'
10
+ export * from './Theme'
11
+ export * from './Validator'
12
+ export * from './Common'
@@ -0,0 +1,99 @@
1
+ /**
2
+ * 聚合函數工具集
3
+ * 用於處理數值陣列的各種聚合運算
4
+ */
5
+
6
+ export type AggregateType = 'sum' | 'mean' | 'median' | 'min' | 'max' | 'count' | 'none'
7
+
8
+ /**
9
+ * 計算數值陣列的總和
10
+ */
11
+ export function sum(values: (number | null)[]): number | null {
12
+ const validValues = values.filter((v): v is number => v !== null && !isNaN(v))
13
+ if (validValues.length === 0) return null
14
+ return validValues.reduce((acc, val) => acc + val, 0)
15
+ }
16
+
17
+ /**
18
+ * 計算數值陣列的平均值
19
+ */
20
+ export function mean(values: (number | null)[]): number | null {
21
+ const validValues = values.filter((v): v is number => v !== null && !isNaN(v))
22
+ if (validValues.length === 0) return null
23
+ const total = validValues.reduce((acc, val) => acc + val, 0)
24
+ return total / validValues.length
25
+ }
26
+
27
+ /**
28
+ * 計算數值陣列的中位數
29
+ */
30
+ export function median(values: (number | null)[]): number | null {
31
+ const validValues = values.filter((v): v is number => v !== null && !isNaN(v))
32
+ if (validValues.length === 0) return null
33
+
34
+ const sorted = [...validValues].sort((a, b) => a - b)
35
+ const mid = Math.floor(sorted.length / 2)
36
+
37
+ if (sorted.length % 2 === 0) {
38
+ return (sorted[mid - 1] + sorted[mid]) / 2
39
+ } else {
40
+ return sorted[mid]
41
+ }
42
+ }
43
+
44
+ /**
45
+ * 計算數值陣列的最小值
46
+ */
47
+ export function min(values: (number | null)[]): number | null {
48
+ const validValues = values.filter((v): v is number => v !== null && !isNaN(v))
49
+ if (validValues.length === 0) return null
50
+ return Math.min(...validValues)
51
+ }
52
+
53
+ /**
54
+ * 計算數值陣列的最大值
55
+ */
56
+ export function max(values: (number | null)[]): number | null {
57
+ const validValues = values.filter((v): v is number => v !== null && !isNaN(v))
58
+ if (validValues.length === 0) return null
59
+ return Math.max(...validValues)
60
+ }
61
+
62
+ /**
63
+ * 計算數值陣列的有效數量
64
+ */
65
+ export function count(values: (number | null)[]): number {
66
+ return values.filter((v): v is number => v !== null && !isNaN(v)).length
67
+ }
68
+
69
+ /**
70
+ * 不進行聚合,返回第一個有效值
71
+ */
72
+ export function none(values: (number | null)[]): number | null {
73
+ const validValues = values.filter((v): v is number => v !== null && !isNaN(v))
74
+ return validValues.length > 0 ? validValues[0] : null
75
+ }
76
+
77
+ /**
78
+ * 根據聚合類型執行對應的聚合函數
79
+ */
80
+ export function aggregate(values: (number | null)[], type: AggregateType): number | null {
81
+ switch (type) {
82
+ case 'sum':
83
+ return sum(values)
84
+ case 'mean':
85
+ return mean(values)
86
+ case 'median':
87
+ return median(values)
88
+ case 'min':
89
+ return min(values)
90
+ case 'max':
91
+ return max(values)
92
+ case 'count':
93
+ return count(values)
94
+ case 'none':
95
+ return none(values)
96
+ default:
97
+ throw new Error(`Unknown aggregate type: ${type}`)
98
+ }
99
+ }
@@ -0,0 +1,63 @@
1
+ import type { Theme } from '../types'
2
+
3
+ /**
4
+ * 根據索引和主題計算對應的顏色
5
+ * @param index 索引值
6
+ * @param theme 主題設定
7
+ * @returns 對應的顏色(hex 格式)
8
+ */
9
+ export function getColorByIndex(index: number, theme: Theme): string {
10
+ // 根據 colorScheme 決定使用哪個色彩方案
11
+ let colorScheme: 'light' | 'dark'
12
+ if (theme.colorScheme === 'auto') {
13
+ // 可以根據系統設定或其他邏輯來決定,這裡預設使用 light
14
+ colorScheme = 'light'
15
+ } else {
16
+ colorScheme = theme.colorScheme
17
+ }
18
+
19
+ const dataColors = theme.colors[colorScheme].data
20
+
21
+ // 使用模運算來循環使用色票
22
+ const colorIndex = index % dataColors.length
23
+ return dataColors[colorIndex]
24
+ }
25
+
26
+ /**
27
+ * 根據不同的 encoding.color.from 計算顏色
28
+ * @param colorFrom 顏色來源類型
29
+ * @param options 包含所有可能索引的選項物件
30
+ * @param theme 主題設定
31
+ * @returns 對應的顏色(hex 格式)
32
+ */
33
+ export function getColorByFrom(
34
+ colorFrom: 'index' | 'series' | 'category' | 'dataset',
35
+ options: {
36
+ index?: number
37
+ seriesIndex?: number
38
+ categoryIndex?: number
39
+ datasetIndex?: number
40
+ },
41
+ theme: Theme
42
+ ): string {
43
+ let targetIndex: number
44
+
45
+ switch (colorFrom) {
46
+ case 'index':
47
+ targetIndex = options.index ?? 0
48
+ break
49
+ case 'series':
50
+ targetIndex = options.seriesIndex ?? 0
51
+ break
52
+ case 'category':
53
+ targetIndex = options.categoryIndex ?? 0
54
+ break
55
+ case 'dataset':
56
+ targetIndex = options.datasetIndex ?? 0
57
+ break
58
+ default:
59
+ targetIndex = 0
60
+ }
61
+
62
+ return getColorByIndex(targetIndex, theme)
63
+ }
@@ -1,3 +1,4 @@
1
+ import { DeepPartial } from "../types/Common";
1
2
 
2
3
  // 是否為原始物件
3
4
  export function isPlainObject(variable: any) {
@@ -16,31 +17,31 @@ export function isDom(obj: any) {
16
17
  }
17
18
 
18
19
  // 將可選的參數和預設值合併
19
- export function mergeOptionsWithDefault<Options extends { [key: string]: any; }> (options: {[key: string]: any}, defaultOptions: Options): Options {
20
- if (isPlainObject(options) === false || isPlainObject(defaultOptions) === false) {
21
- return Object.assign({}, defaultOptions)
20
+ export function deepOverwrite<DeepRecord extends Record<string, any>>(full: DeepRecord, options: DeepPartial<DeepRecord>): DeepRecord {
21
+ if (isPlainObject(options) === false || isPlainObject(full) === false) {
22
+ return Object.assign({}, full)
22
23
  }
23
- const mergeObjColumns = (_options: {[key: string]: any}, _defaultOptions: {[key: string]: any}) => {
24
- const obj: Options = (Object.assign({}, _defaultOptions) as any)
24
+ const mergeObject = (_full: DeepRecord, _options: DeepPartial<DeepRecord>) => {
25
+ const obj: DeepRecord = (Object.assign({}, _full) as any)
25
26
  for (let key of Object.keys(_options)) {
26
- if ((key in _defaultOptions) == false) {
27
+ if ((key in _full) == false) {
27
28
  continue
28
29
  }
29
30
  let objValue: any = undefined
30
31
  // 下一層的plain object
31
- if (isPlainObject(_options[key]) && isPlainObject(_defaultOptions[key])) {
32
- objValue = mergeObjColumns(_options[key], _defaultOptions[key])
33
- obj[key as keyof Options] = objValue
32
+ if (isPlainObject(_options[key]) && isPlainObject(_full[key])) {
33
+ objValue = mergeObject(_full[key], _options[key])
34
+ obj[key as keyof DeepRecord] = objValue
34
35
  }
35
36
  // 不是plain object直接賦值
36
37
  else {
37
- obj[key as keyof Options] = _options[key]
38
+ obj[key as keyof DeepRecord] = _options[key] as DeepRecord[typeof key]
38
39
  }
39
40
  }
40
41
  return obj
41
42
  }
42
43
 
43
- return mergeObjColumns(options, defaultOptions)
44
+ return mergeObject(full, options)
44
45
  }
45
46
 
46
47
  // 加上千分位 ,
@@ -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
+ }