@363045841yyt/klinechart 0.8.1-alpha.4 → 0.8.1
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.
- package/dist/components/CompareSymbolSelector.vue.d.ts +2 -0
- package/dist/components/CompareSymbolSelector.vue.d.ts.map +1 -1
- package/dist/components/KLineChart.vue.d.ts.map +1 -1
- package/dist/components/SymbolSelector.vue.d.ts.map +1 -1
- package/dist/components/TopToolbar.vue.d.ts +2 -0
- package/dist/components/TopToolbar.vue.d.ts.map +1 -1
- package/dist/index.cjs +2 -2
- package/dist/index.css +1 -1
- package/dist/index.js +356 -323
- package/package.json +1 -1
- package/src/components/CompareSymbolSelector.vue +29 -0
- package/src/components/KLineChart.vue +20 -2
- package/src/components/SymbolSelector.vue +12 -3
- package/src/components/TopToolbar.vue +4 -0
package/package.json
CHANGED
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
>
|
|
12
12
|
<span class="compare-chip__icon" aria-hidden="true">+</span>
|
|
13
13
|
<span class="compare-chip__text">比较商品</span>
|
|
14
|
+
<span v-if="comparisonLoading" class="compare-chip__spinner" />
|
|
14
15
|
<span v-if="selected.length > 0" class="compare-chip__badge">{{ selected.length }}</span>
|
|
15
16
|
</button>
|
|
16
17
|
<Transition name="symbol-popover">
|
|
@@ -65,6 +66,10 @@
|
|
|
65
66
|
:key="item.code"
|
|
66
67
|
class="compare-selected__item"
|
|
67
68
|
>
|
|
69
|
+
<span
|
|
70
|
+
class="compare-selected__color"
|
|
71
|
+
:style="{ background: comparisonColors?.get(item.code) ?? '#888' }"
|
|
72
|
+
/>
|
|
68
73
|
<span class="compare-selected__code">{{ item.code }}</span>
|
|
69
74
|
<span class="compare-selected__desc">{{ item.description }}</span>
|
|
70
75
|
<button
|
|
@@ -140,6 +145,8 @@ import type { SymbolItem } from './SymbolSelector.vue'
|
|
|
140
145
|
const props = withDefaults(defineProps<{
|
|
141
146
|
symbols: SymbolItem[]
|
|
142
147
|
selected?: string[]
|
|
148
|
+
comparisonColors?: Map<string, string>
|
|
149
|
+
comparisonLoading?: boolean
|
|
143
150
|
}>(), {
|
|
144
151
|
selected: () => [],
|
|
145
152
|
})
|
|
@@ -280,6 +287,20 @@ onBeforeUnmount(() => document.removeEventListener('mousedown', onDocumentClick)
|
|
|
280
287
|
line-height: 1;
|
|
281
288
|
}
|
|
282
289
|
|
|
290
|
+
.compare-chip__spinner {
|
|
291
|
+
display: inline-block;
|
|
292
|
+
width: 12px;
|
|
293
|
+
height: 12px;
|
|
294
|
+
border: 2px solid var(--klc-color-axis-text);
|
|
295
|
+
border-top-color: transparent;
|
|
296
|
+
border-radius: 50%;
|
|
297
|
+
animation: compare-spin 0.6s linear infinite;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
@keyframes compare-spin {
|
|
301
|
+
to { transform: rotate(360deg); }
|
|
302
|
+
}
|
|
303
|
+
|
|
283
304
|
.compare-popover {
|
|
284
305
|
position: absolute;
|
|
285
306
|
top: calc(100% + 8px);
|
|
@@ -406,6 +427,14 @@ onBeforeUnmount(() => document.removeEventListener('mousedown', onDocumentClick)
|
|
|
406
427
|
line-height: 1.3;
|
|
407
428
|
}
|
|
408
429
|
|
|
430
|
+
.compare-selected__color {
|
|
431
|
+
display: inline-block;
|
|
432
|
+
width: 8px;
|
|
433
|
+
height: 8px;
|
|
434
|
+
border-radius: 50%;
|
|
435
|
+
flex-shrink: 0;
|
|
436
|
+
}
|
|
437
|
+
|
|
409
438
|
.compare-selected__code {
|
|
410
439
|
font-weight: 600;
|
|
411
440
|
color: var(--klc-color-foreground);
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
:symbol-loading="symbolLoading"
|
|
7
7
|
:symbol-error="symbolError"
|
|
8
8
|
:overlay-symbols="overlaySymbols"
|
|
9
|
+
:comparison-colors="comparisonColorsMap"
|
|
10
|
+
:comparison-loading="comparisonLoading"
|
|
9
11
|
@add-overlay-symbol="onAddOverlaySymbol"
|
|
10
12
|
@remove-overlay-symbol="onRemoveOverlaySymbol"
|
|
11
13
|
@k-line-level-change="onKLineLevelChange"
|
|
@@ -152,6 +154,7 @@ import {
|
|
|
152
154
|
getRegisteredIndicatorDefinitions,
|
|
153
155
|
} from '@363045841yyt/klinechart-core/indicators'
|
|
154
156
|
import type { DrawingObject, DrawingStyle } from '@363045841yyt/klinechart-core/plugin'
|
|
157
|
+
import { SETTINGS_STORAGE_KEY } from '@363045841yyt/klinechart-core/config'
|
|
155
158
|
import type { ChartSettings } from '@363045841yyt/klinechart-core/config'
|
|
156
159
|
import {
|
|
157
160
|
resolveThemeColors,
|
|
@@ -235,13 +238,13 @@ function onAddOverlaySymbol(item: SymbolItem) {
|
|
|
235
238
|
overlaySymbolItems.value = [...overlaySymbolItems.value, item]
|
|
236
239
|
overlaySymbols.value = overlaySymbolItems.value.map((symbol) => symbol.code)
|
|
237
240
|
forcePercentAxis()
|
|
238
|
-
|
|
241
|
+
controller.value?.addComparisonSymbol(toSymbolSpec(item))
|
|
239
242
|
}
|
|
240
243
|
|
|
241
244
|
function onRemoveOverlaySymbol(code: string) {
|
|
242
245
|
overlaySymbolItems.value = overlaySymbolItems.value.filter((item) => item.code !== code)
|
|
243
246
|
overlaySymbols.value = overlaySymbolItems.value.map((symbol) => symbol.code)
|
|
244
|
-
|
|
247
|
+
controller.value?.removeComparisonSymbol(code)
|
|
245
248
|
}
|
|
246
249
|
|
|
247
250
|
function toSymbolSpec(item: SymbolItem): SymbolSpec {
|
|
@@ -269,6 +272,9 @@ function forcePercentAxis() {
|
|
|
269
272
|
const nextSettings = { ...chartSettings.value, axisType: 'percent' as const }
|
|
270
273
|
chartSettings.value = nextSettings
|
|
271
274
|
controller.value?.updateSettingsFacade(nextSettings)
|
|
275
|
+
try {
|
|
276
|
+
localStorage.setItem(SETTINGS_STORAGE_KEY, JSON.stringify(nextSettings))
|
|
277
|
+
} catch { /* quota exceeded */ }
|
|
272
278
|
}
|
|
273
279
|
|
|
274
280
|
const containerRef = ref<HTMLDivElement | null>(null)
|
|
@@ -296,6 +302,8 @@ const viewWidth = ref(0)
|
|
|
296
302
|
const paneRatios = ref<Record<string, number>>({})
|
|
297
303
|
const selectedDrawingId = ref<string | null>(null)
|
|
298
304
|
const drawings = ref<DrawingObject[]>([])
|
|
305
|
+
const comparisonColorsMap = ref<Map<string, string>>(new Map())
|
|
306
|
+
const comparisonLoading = ref(false)
|
|
299
307
|
|
|
300
308
|
// 初始化 kWidth / kGap(与 Chart 引擎 zoom→物理值 转换一致)
|
|
301
309
|
const initZoom = zoomLevel.value
|
|
@@ -983,6 +991,14 @@ function setupChartCallbacks(ctrl: ChartController): void {
|
|
|
983
991
|
indicatorParams.value = nextParams
|
|
984
992
|
})
|
|
985
993
|
|
|
994
|
+
const unsubscribeComparisonColors = ctrl.comparisonColors.subscribe(() => {
|
|
995
|
+
comparisonColorsMap.value = new Map(ctrl.comparisonColors.peek())
|
|
996
|
+
})
|
|
997
|
+
|
|
998
|
+
const unsubscribeComparisonLoading = ctrl.comparisonLoading.subscribe(() => {
|
|
999
|
+
comparisonLoading.value = ctrl.comparisonLoading.peek()
|
|
1000
|
+
})
|
|
1001
|
+
|
|
986
1002
|
onUnmounted(() => {
|
|
987
1003
|
unsubscribeViewport()
|
|
988
1004
|
unsubscribeData()
|
|
@@ -992,6 +1008,8 @@ function setupChartCallbacks(ctrl: ChartController): void {
|
|
|
992
1008
|
unsubscribeTheme()
|
|
993
1009
|
unsubscribeIndicators()
|
|
994
1010
|
unsubscribeSubPanes()
|
|
1011
|
+
unsubscribeComparisonColors()
|
|
1012
|
+
unsubscribeComparisonLoading()
|
|
995
1013
|
autoThemeMediaQuery?.removeEventListener('change', onSystemThemeChange)
|
|
996
1014
|
})
|
|
997
1015
|
}
|
|
@@ -4,12 +4,12 @@
|
|
|
4
4
|
type="button"
|
|
5
5
|
class="symbol-chip"
|
|
6
6
|
:class="{ 'is-open': showPopup }"
|
|
7
|
-
:title="
|
|
7
|
+
:title="displayText"
|
|
8
8
|
:aria-expanded="showPopup"
|
|
9
9
|
aria-haspopup="dialog"
|
|
10
10
|
@click="togglePopup"
|
|
11
11
|
>
|
|
12
|
-
<span class="symbol-chip__code">{{
|
|
12
|
+
<span class="symbol-chip__code">{{ displayText }}</span>
|
|
13
13
|
<span v-if="loading" class="symbol-chip__spinner" aria-hidden="true" />
|
|
14
14
|
<IconTablerAlertTriangle v-else-if="error" class="symbol-chip__warn" aria-hidden="true" />
|
|
15
15
|
</button>
|
|
@@ -127,6 +127,16 @@ const searchQuery = ref('')
|
|
|
127
127
|
const searchInputRef = ref<HTMLInputElement | null>(null)
|
|
128
128
|
const chipWrapRef = ref<HTMLElement | null>(null)
|
|
129
129
|
|
|
130
|
+
const currentSymbol = computed<SymbolItem | undefined>(() =>
|
|
131
|
+
props.symbols.find((s) => s.code === props.symbol),
|
|
132
|
+
)
|
|
133
|
+
|
|
134
|
+
const displayText = computed(() => {
|
|
135
|
+
const cur = currentSymbol.value
|
|
136
|
+
if (cur) return `${cur.code} - ${cur.description}`
|
|
137
|
+
return props.symbol
|
|
138
|
+
})
|
|
139
|
+
|
|
130
140
|
const filteredSymbols = computed<SymbolItem[]>(() => {
|
|
131
141
|
const q = searchQuery.value.trim().toLowerCase()
|
|
132
142
|
if (!q) return props.symbols
|
|
@@ -186,7 +196,6 @@ watch(() => props.symbol, () => {
|
|
|
186
196
|
display: inline-flex;
|
|
187
197
|
align-items: center;
|
|
188
198
|
justify-content: center;
|
|
189
|
-
max-width: 160px;
|
|
190
199
|
padding: 0 10px;
|
|
191
200
|
gap: 5px;
|
|
192
201
|
border: 1px solid transparent;
|
|
@@ -11,6 +11,8 @@
|
|
|
11
11
|
<CompareSymbolSelector
|
|
12
12
|
:symbols="symbolPool"
|
|
13
13
|
:selected="overlaySymbols"
|
|
14
|
+
:comparison-colors="comparisonColors"
|
|
15
|
+
:comparison-loading="comparisonLoading"
|
|
14
16
|
@add="emit('addOverlaySymbol', $event)"
|
|
15
17
|
@remove="emit('removeOverlaySymbol', $event)"
|
|
16
18
|
/>
|
|
@@ -47,6 +49,8 @@ const props = defineProps<{
|
|
|
47
49
|
symbolLoading?: boolean
|
|
48
50
|
symbolError?: boolean
|
|
49
51
|
overlaySymbols?: string[]
|
|
52
|
+
comparisonColors?: Map<string, string>
|
|
53
|
+
comparisonLoading?: boolean
|
|
50
54
|
}>()
|
|
51
55
|
|
|
52
56
|
const emit = defineEmits<{
|