@363045841yyt/klinechart 0.8.4 → 0.8.6

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 (69) hide show
  1. package/README.md +6 -1
  2. package/dist/components/BaseModal.vue.d.ts +54 -0
  3. package/dist/components/BaseModal.vue.d.ts.map +1 -0
  4. package/dist/components/BatchStockDialog.vue.d.ts +13 -0
  5. package/dist/components/BatchStockDialog.vue.d.ts.map +1 -0
  6. package/dist/components/ChartSettingsDialog.vue.d.ts.map +1 -1
  7. package/dist/components/ColorPresetPanel.vue.d.ts +4 -1
  8. package/dist/components/ColorPresetPanel.vue.d.ts.map +1 -1
  9. package/dist/components/CompareSymbolSelector.vue.d.ts.map +1 -1
  10. package/dist/components/DrawingStyleToolbar.vue.d.ts.map +1 -1
  11. package/dist/components/Dropdown.vue.d.ts.map +1 -1
  12. package/dist/components/ExportProgressDialog.vue.d.ts +15 -0
  13. package/dist/components/ExportProgressDialog.vue.d.ts.map +1 -0
  14. package/dist/components/IndicatorParams.vue.d.ts.map +1 -1
  15. package/dist/components/IndicatorSelector.vue.d.ts.map +1 -1
  16. package/dist/components/KLineChart.vue.d.ts +5 -9
  17. package/dist/components/KLineChart.vue.d.ts.map +1 -1
  18. package/dist/components/LeftToolbar.vue.d.ts.map +1 -1
  19. package/dist/components/RangeSelectionExport.vue.d.ts +23 -0
  20. package/dist/components/RangeSelectionExport.vue.d.ts.map +1 -0
  21. package/dist/components/SymbolSelector.vue.d.ts.map +1 -1
  22. package/dist/components/TopToolbar.vue.d.ts.map +1 -1
  23. package/dist/components/common/CanvasToolbar.vue.d.ts +14 -0
  24. package/dist/components/common/CanvasToolbar.vue.d.ts.map +1 -0
  25. package/dist/components/common/CanvasToolbarStack.vue.d.ts +14 -0
  26. package/dist/components/common/CanvasToolbarStack.vue.d.ts.map +1 -0
  27. package/dist/composables/chart/useChartTheme.d.ts +329 -0
  28. package/dist/composables/chart/useChartTheme.d.ts.map +1 -0
  29. package/dist/composables/chart/useDrawingManager.d.ts +86 -0
  30. package/dist/composables/chart/useDrawingManager.d.ts.map +1 -0
  31. package/dist/composables/chart/useIndicatorManager.d.ts +38 -0
  32. package/dist/composables/chart/useIndicatorManager.d.ts.map +1 -0
  33. package/dist/composables/chart/useRangeSelection.d.ts +66 -0
  34. package/dist/composables/chart/useRangeSelection.d.ts.map +1 -0
  35. package/dist/composables/useTeleportedPopup.d.ts +8 -0
  36. package/dist/composables/useTeleportedPopup.d.ts.map +1 -0
  37. package/dist/index.cjs +9 -2
  38. package/dist/index.css +1 -1
  39. package/dist/index.js +2149 -1409
  40. package/dist/tools/calcRangeOverlayPixel.d.ts +15 -0
  41. package/dist/tools/calcRangeOverlayPixel.d.ts.map +1 -0
  42. package/dist/tools/getKLineIndexByTimestamp.d.ts +4 -0
  43. package/dist/tools/getKLineIndexByTimestamp.d.ts.map +1 -0
  44. package/dist/web-component.d.ts.map +1 -1
  45. package/package.json +1 -1
  46. package/src/components/BaseModal.vue +292 -0
  47. package/src/components/BatchStockDialog.vue +128 -0
  48. package/src/components/ChartSettingsDialog.vue +248 -405
  49. package/src/components/ColorPresetPanel.vue +58 -106
  50. package/src/components/CompareSymbolSelector.vue +37 -10
  51. package/src/components/DrawingStyleToolbar.vue +33 -72
  52. package/src/components/Dropdown.vue +42 -19
  53. package/src/components/ExportProgressDialog.vue +118 -0
  54. package/src/components/IndicatorParams.vue +194 -321
  55. package/src/components/IndicatorSelector.vue +188 -405
  56. package/src/components/KLineChart.vue +228 -403
  57. package/src/components/LeftToolbar.vue +3 -2
  58. package/src/components/RangeSelectionExport.vue +117 -0
  59. package/src/components/SymbolSelector.vue +37 -10
  60. package/src/components/TopToolbar.vue +55 -2
  61. package/src/components/common/CanvasToolbar.vue +70 -0
  62. package/src/components/common/CanvasToolbarStack.vue +32 -0
  63. package/src/composables/chart/useChartTheme.ts +86 -0
  64. package/src/composables/chart/useDrawingManager.ts +67 -0
  65. package/src/composables/chart/useIndicatorManager.ts +307 -0
  66. package/src/composables/chart/useRangeSelection.ts +424 -0
  67. package/src/composables/useTeleportedPopup.ts +46 -0
  68. package/src/tools/calcRangeOverlayPixel.ts +28 -0
  69. package/src/tools/getKLineIndexByTimestamp.ts +40 -0
@@ -0,0 +1,307 @@
1
+ /**
2
+ * Manages indicator state for both main-pane and sub-pane indicators.
3
+ * Provides pane layout construction, default param resolution,
4
+ * indicator toggle/update/reorder logic, and bridges signal subscriptions
5
+ * (ctrl.indicators, ctrl.subPanes) to Vue reactive refs.
6
+ */
7
+ import { ref, computed, type Ref } from 'vue'
8
+ import type {
9
+ ChartController,
10
+ PaneSpec,
11
+ IndicatorInstance,
12
+ SubIndicatorType,
13
+ } from '@363045841yyt/klinechart-core/controllers'
14
+ import { getRegisteredIndicatorDefinition } from '@363045841yyt/klinechart-core/indicators'
15
+ import type { SemanticChartConfig } from '@363045841yyt/klinechart-core/semantic'
16
+
17
+ interface SubPaneSlot {
18
+ id: string
19
+ indicatorId: SubIndicatorType
20
+ params: Record<string, unknown>
21
+ }
22
+
23
+ export function useIndicatorManager(
24
+ ctrl: Ref<ChartController | null>,
25
+ paneRatiosRef: Ref<Record<string, number>>,
26
+ ) {
27
+ const maxSubPanes = 4
28
+
29
+ const mainActiveIndicators = ref<string[]>([])
30
+
31
+ const subPanes = ref<SubPaneSlot[]>([])
32
+
33
+ const subActiveIndicators = computed(() => {
34
+ const ids: string[] = []
35
+ const seen = new Set<string>()
36
+ for (const pane of subPanes.value) {
37
+ if (!seen.has(pane.indicatorId)) {
38
+ seen.add(pane.indicatorId)
39
+ ids.push(pane.indicatorId)
40
+ }
41
+ }
42
+ return ids
43
+ })
44
+
45
+ const activeIndicators = computed(() => [
46
+ ...mainActiveIndicators.value,
47
+ ...subActiveIndicators.value,
48
+ ])
49
+
50
+ const indicatorParams = ref<Record<string, Record<string, unknown>>>({})
51
+
52
+ function buildPaneLayoutIntent(): PaneSpec[] {
53
+ const mainRatio = paneRatiosRef.value['main'] ?? 3
54
+ return subPanes.value.length === 0
55
+ ? [{ id: 'main', ratio: mainRatio, visible: true, role: 'price' }]
56
+ : [
57
+ { id: 'main', ratio: mainRatio, visible: true, role: 'price' },
58
+ ...subPanes.value.map((pane) => ({
59
+ id: pane.id,
60
+ ratio: paneRatiosRef.value[pane.id] ?? 1,
61
+ visible: true,
62
+ role: 'indicator' as const,
63
+ })),
64
+ ]
65
+ }
66
+
67
+ function getDefaultParams(
68
+ indicatorId: SubIndicatorType,
69
+ ): Record<string, number | boolean | string> {
70
+ if (indicatorId === 'VOLUME') return {}
71
+ const meta = getRegisteredIndicatorDefinition(indicatorId)
72
+ if (meta?.runtime?.defaultConfig) {
73
+ return { ...meta.runtime.defaultConfig } as Record<string, number | boolean | string>
74
+ }
75
+ return {}
76
+ }
77
+
78
+ function isSubPaneIndicator(id: string): boolean {
79
+ if (id === 'VOLUME') return true
80
+ const def = getRegisteredIndicatorDefinition(id)
81
+ return !!def && def.category !== 'main'
82
+ }
83
+
84
+ function addSubPane(
85
+ indicatorId: SubIndicatorType = 'VOLUME',
86
+ params?: Record<string, number | boolean | string>,
87
+ ): boolean {
88
+ if (subPanes.value.length >= maxSubPanes) {
89
+ return false
90
+ }
91
+
92
+ const mergedParams = params ?? getDefaultParams(indicatorId)
93
+
94
+ const paneId = ctrl.value?.addIndicator(indicatorId, 'sub', mergedParams)
95
+ if (!paneId) return false
96
+ return true
97
+ }
98
+
99
+ function removeSubPane(paneId: string): void {
100
+ ctrl.value?.removeIndicator(paneId)
101
+ }
102
+
103
+ function clearAllSubPanes(): void {
104
+ for (const pane of subPanes.value) {
105
+ ctrl.value?.removeIndicator(pane.id)
106
+ }
107
+ }
108
+
109
+ function initIndicatorsFromConfig(semanticConfig?: SemanticChartConfig): void {
110
+ const config = semanticConfig
111
+ const c = ctrl.value
112
+ if (!config || !c) return
113
+
114
+ const mainIndicators = config.indicators?.main
115
+ if (mainIndicators) {
116
+ for (const indicator of mainIndicators) {
117
+ if (indicator.enabled) {
118
+ c.addIndicator(
119
+ indicator.type,
120
+ 'main',
121
+ indicator.params as Record<string, number | boolean | string>,
122
+ )
123
+ }
124
+ }
125
+ }
126
+ }
127
+
128
+ function switchSubIndicator(paneId: string, newIndicatorId: SubIndicatorType): void {
129
+ const nextParams = getDefaultParams(newIndicatorId)
130
+ ctrl.value?.replaceSubPaneIndicator(paneId, newIndicatorId, nextParams)
131
+ }
132
+
133
+ function handleIndicatorToggle(indicatorId: string, active: boolean) {
134
+ const c = ctrl.value
135
+ if (!c) return
136
+
137
+ const def = getRegisteredIndicatorDefinition(indicatorId)
138
+ const isMain = def && (def.category === 'main' || def.allowMainPane)
139
+ if (isMain) {
140
+ const existingIndicator = mainActiveIndicators.value.find((id) => id === indicatorId)
141
+ if (active && !existingIndicator) {
142
+ c.addIndicator(indicatorId, 'main', indicatorParams.value[indicatorId])
143
+ } else if (!active && existingIndicator) {
144
+ c.removeIndicator(indicatorId.toUpperCase())
145
+ }
146
+ return
147
+ }
148
+
149
+ if (isSubPaneIndicator(indicatorId)) {
150
+ if (active) {
151
+ const existingPane = subPanes.value.find((p) => p.indicatorId === indicatorId)
152
+ if (existingPane) return
153
+ if (subPanes.value.length >= maxSubPanes) return
154
+
155
+ const paneId = c.addIndicator(indicatorId, 'sub', indicatorParams.value[indicatorId])
156
+ if (!paneId && subPanes.value.length > 0) {
157
+ const lastPane = subPanes.value[subPanes.value.length - 1]
158
+ switchSubIndicator(lastPane.id, indicatorId as SubIndicatorType)
159
+ }
160
+ } else {
161
+ const panesToRemove = subPanes.value.filter((p) => p.indicatorId === indicatorId)
162
+ panesToRemove.forEach((pane) => {
163
+ c.removeIndicator(pane.id)
164
+ })
165
+ }
166
+ }
167
+ }
168
+
169
+ function handleUpdateParams(indicatorId: string, params: Record<string, unknown>) {
170
+ if (
171
+ indicatorId === 'MA' ||
172
+ indicatorId === 'BOLL' ||
173
+ indicatorId === 'EXPMA' ||
174
+ indicatorId === 'ENE'
175
+ ) {
176
+ ctrl.value?.updateIndicatorParams(indicatorId, params)
177
+ return
178
+ }
179
+ if (isSubPaneIndicator(indicatorId)) {
180
+ subPanes.value
181
+ .filter((p) => p.indicatorId === indicatorId)
182
+ .forEach((pane) => {
183
+ ctrl.value?.updateIndicatorParams(pane.id, params)
184
+ })
185
+ }
186
+ }
187
+
188
+ function handleReorderSubIndicators(orderedIndicatorIds: string[]) {
189
+ if (!orderedIndicatorIds.length || subPanes.value.length <= 1) return
190
+
191
+ const validOrder = orderedIndicatorIds.filter((id): id is SubIndicatorType =>
192
+ isSubPaneIndicator(id),
193
+ )
194
+ if (!validOrder.length) return
195
+
196
+ const paneByIndicator = new Map(subPanes.value.map((pane) => [pane.indicatorId, pane] as const))
197
+ const nextSubPanes: SubPaneSlot[] = []
198
+
199
+ for (const indicatorId of validOrder) {
200
+ const pane = paneByIndicator.get(indicatorId)
201
+ if (pane) {
202
+ nextSubPanes.push(pane)
203
+ paneByIndicator.delete(indicatorId)
204
+ }
205
+ }
206
+
207
+ if (nextSubPanes.length === 0) return
208
+
209
+ for (const pane of subPanes.value) {
210
+ if (paneByIndicator.has(pane.indicatorId)) {
211
+ nextSubPanes.push(pane)
212
+ paneByIndicator.delete(pane.indicatorId)
213
+ }
214
+ }
215
+
216
+ const currentSubIds = subPanes.value.map((p) => p.id)
217
+ const nextSubIds = nextSubPanes.map((p) => p.id)
218
+ if (currentSubIds.join('|') === nextSubIds.join('|')) return
219
+
220
+ subPanes.value = nextSubPanes
221
+
222
+ const c = ctrl.value
223
+ if (!c) return
224
+ c.updatePaneLayout(buildPaneLayoutIntent())
225
+ }
226
+
227
+ function setupIndicatorSubscriptions(chartCtrl: ChartController): () => void {
228
+ const unsubIndicators = chartCtrl.indicators.subscribe(() => {
229
+ const instances = chartCtrl.indicators.peek()
230
+
231
+ const mains = instances
232
+ .filter((i): i is IndicatorInstance & { role: 'main' } => i.role === 'main')
233
+ .map((i) => i.definitionId)
234
+ mainActiveIndicators.value = mains
235
+
236
+ const nextParams = { ...indicatorParams.value }
237
+ for (const inst of instances) {
238
+ if (inst.role === 'main' && inst.params && Object.keys(inst.params).length > 0) {
239
+ nextParams[inst.definitionId] = { ...inst.params }
240
+ }
241
+ }
242
+
243
+ chartCtrl.updateRendererConfig('mainIndicatorLegend', {
244
+ indicators: {
245
+ MA: { enabled: mains.includes('MA'), params: nextParams['MA'] || {} },
246
+ BOLL: { enabled: mains.includes('BOLL'), params: nextParams['BOLL'] || {} },
247
+ EXPMA: { enabled: mains.includes('EXPMA'), params: nextParams['EXPMA'] || {} },
248
+ ENE: { enabled: mains.includes('ENE'), params: nextParams['ENE'] || {} },
249
+ },
250
+ })
251
+
252
+ indicatorParams.value = nextParams
253
+ })
254
+
255
+ const unsubSubPanes = chartCtrl.subPanes.subscribe(() => {
256
+ const subPaneInfos = chartCtrl.subPanes.peek()
257
+ const signalIds = new Set(subPaneInfos.map((sp) => sp.paneId))
258
+
259
+ const merged = subPanes.value.filter((p) => signalIds.has(p.id))
260
+ const existingIds = new Set(merged.map((p) => p.id))
261
+ for (const sp of subPaneInfos) {
262
+ if (!existingIds.has(sp.paneId)) {
263
+ merged.push({
264
+ id: sp.paneId,
265
+ indicatorId: sp.indicatorId as SubIndicatorType,
266
+ params: sp.params,
267
+ })
268
+ }
269
+ }
270
+ subPanes.value = merged
271
+
272
+ const nextParams = { ...indicatorParams.value }
273
+ for (const sp of subPaneInfos) {
274
+ if (sp.params && Object.keys(sp.params).length > 0) {
275
+ nextParams[sp.indicatorId] = { ...sp.params }
276
+ }
277
+ }
278
+ indicatorParams.value = nextParams
279
+ })
280
+
281
+ return () => {
282
+ unsubIndicators()
283
+ unsubSubPanes()
284
+ }
285
+ }
286
+
287
+ return {
288
+ mainActiveIndicators,
289
+ subActiveIndicators,
290
+ activeIndicators,
291
+ indicatorParams,
292
+ subPanes,
293
+ maxSubPanes,
294
+ buildPaneLayoutIntent,
295
+ getDefaultParams,
296
+ isSubPaneIndicator,
297
+ addSubPane,
298
+ removeSubPane,
299
+ clearAllSubPanes,
300
+ initIndicatorsFromConfig,
301
+ switchSubIndicator,
302
+ handleIndicatorToggle,
303
+ handleUpdateParams,
304
+ handleReorderSubIndicators,
305
+ setupIndicatorSubscriptions,
306
+ }
307
+ }