@363045841yyt/klinechart 0.7.5 → 0.7.7

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 (43) hide show
  1. package/README.md +4 -4
  2. package/dist/components/ChartSettingsDialog.vue.d.ts +14 -0
  3. package/dist/components/ChartSettingsDialog.vue.d.ts.map +1 -0
  4. package/dist/components/ColorPresetPanel.vue.d.ts +12 -0
  5. package/dist/components/ColorPresetPanel.vue.d.ts.map +1 -0
  6. package/dist/components/DrawingStyleToolbar.vue.d.ts +1 -15
  7. package/dist/components/DrawingStyleToolbar.vue.d.ts.map +1 -1
  8. package/dist/components/IndicatorParams.vue.d.ts.map +1 -1
  9. package/dist/components/IndicatorSelector.vue.d.ts.map +1 -1
  10. package/dist/components/KLineChart.vue.d.ts +7 -6
  11. package/dist/components/KLineChart.vue.d.ts.map +1 -1
  12. package/dist/components/KLineTooltip.vue.d.ts +9 -2
  13. package/dist/components/KLineTooltip.vue.d.ts.map +1 -1
  14. package/dist/components/LeftToolbar.vue.d.ts +4 -3
  15. package/dist/components/LeftToolbar.vue.d.ts.map +1 -1
  16. package/dist/components/MarkerTooltip.vue.d.ts +1 -12
  17. package/dist/components/MarkerTooltip.vue.d.ts.map +1 -1
  18. package/dist/components/index.d.ts +1 -0
  19. package/dist/components/index.d.ts.map +1 -1
  20. package/dist/composables/useFullscreenTeleportTarget.d.ts.map +1 -1
  21. package/dist/index.cjs +2 -2
  22. package/dist/index.css +1 -0
  23. package/dist/index.d.cts +5 -5
  24. package/dist/index.d.ts +5 -5
  25. package/dist/index.js +1009 -905
  26. package/dist/version.d.ts +1 -1
  27. package/dist/web-component.d.ts +18 -0
  28. package/dist/web-component.d.ts.map +1 -0
  29. package/package.json +10 -2
  30. package/src/__tests__/_mockController.ts +11 -1
  31. package/src/components/ChartSettingsDialog.vue +624 -0
  32. package/src/components/ColorPresetPanel.vue +289 -0
  33. package/src/components/DrawingStyleToolbar.vue +12 -25
  34. package/src/components/IndicatorParams.vue +58 -57
  35. package/src/components/IndicatorSelector.vue +91 -88
  36. package/src/components/KLineChart.vue +267 -442
  37. package/src/components/KLineTooltip.vue +19 -13
  38. package/src/components/LeftToolbar.vue +35 -393
  39. package/src/components/MarkerTooltip.vue +5 -16
  40. package/src/components/index.ts +1 -0
  41. package/src/composables/useFullscreenTeleportTarget.ts +0 -2
  42. package/src/web-component.ts +14 -0
  43. package/dist/klinechart.css +0 -2
@@ -67,15 +67,22 @@ export interface KLineData {
67
67
  stockCode?: string
68
68
  }
69
69
 
70
- const props = defineProps<{
70
+ const props = withDefaults(defineProps<{
71
71
  k: KLineData | null
72
72
  index: number | null
73
- data: KLineData[]
73
+ data: ReadonlyArray<KLineData>
74
74
  pos: { x: number; y: number }
75
75
  useAnchor?: boolean
76
76
  anchorPlacement?: 'right-bottom' | 'left-bottom'
77
77
  setEl?: (el: HTMLDivElement | null) => void
78
- }>()
78
+ /** 涨的颜色(默认红涨) */
79
+ upColor?: string
80
+ /** 跌的颜色(默认绿跌) */
81
+ downColor?: string
82
+ }>(), {
83
+ upColor: '#ef4444',
84
+ downColor: '#22c55e',
85
+ })
79
86
 
80
87
  const useAnchor = computed(() => props.useAnchor === true)
81
88
  const anchorPlacementClass = computed(() =>
@@ -105,11 +112,9 @@ function formatSigned(val: number, unit: string): string {
105
112
  return `${sign}${val.toFixed(2)}${unit}`
106
113
  }
107
114
 
108
- const UP_COLOR = '#ef4444'
109
- const DOWN_COLOR = '#22c55e'
110
115
  const NEUTRAL_COLOR = '#6b7280'
111
116
 
112
- function calcDirection(k: KLineData, data: KLineData[], idx: number | null): number {
117
+ function calcDirection(k: KLineData, data: ReadonlyArray<KLineData>, idx: number | null): number {
113
118
  if (k.close >= k.open) return 1
114
119
  const prev = typeof idx === 'number' && idx > 0 ? data[idx - 1] : undefined
115
120
  if (prev && k.close > prev.close) return 1
@@ -121,21 +126,21 @@ const openColor = computed(() => {
121
126
  const k = props.k
122
127
  if (!k) return NEUTRAL_COLOR
123
128
  const dir = calcDirection(k, props.data, props.index)
124
- return dir > 0 ? UP_COLOR : dir < 0 ? DOWN_COLOR : NEUTRAL_COLOR
129
+ return dir > 0 ? props.upColor : dir < 0 ? props.downColor : NEUTRAL_COLOR
125
130
  })
126
131
 
127
132
  const closeColor = computed(() => {
128
133
  const k = props.k
129
134
  if (!k) return NEUTRAL_COLOR
130
135
  const diff = k.close - k.open
131
- return diff > 0 ? UP_COLOR : diff < 0 ? DOWN_COLOR : NEUTRAL_COLOR
136
+ return diff > 0 ? props.upColor : diff < 0 ? props.downColor : NEUTRAL_COLOR
132
137
  })
133
138
 
134
139
  const changeColor = computed(() => {
135
140
  const k = props.k
136
141
  if (!k) return NEUTRAL_COLOR
137
142
  const pct = k.changePercent ?? (k.close - k.open) / k.open * 100
138
- return pct > 0 ? UP_COLOR : pct < 0 ? DOWN_COLOR : NEUTRAL_COLOR
143
+ return pct > 0 ? props.upColor : pct < 0 ? props.downColor : NEUTRAL_COLOR
139
144
  })
140
145
  </script>
141
146
 
@@ -147,10 +152,10 @@ const changeColor = computed(() => {
147
152
  max-width: 260px;
148
153
  padding: 10px 12px;
149
154
  border-radius: 8px;
150
- background: rgba(255, 255, 255, 0.92);
151
- border: 1px solid rgba(0, 0, 0, 0.12);
155
+ background: var(--klc-color-tooltip-bg);
156
+ border: 1px solid var(--klc-color-tooltip-border);
152
157
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.12);
153
- color: rgba(0, 0, 0, 0.78);
158
+ color: var(--klc-color-tooltip-text);
154
159
  font-size: 12px;
155
160
  line-height: 1.4;
156
161
  pointer-events: none;
@@ -178,7 +183,8 @@ const changeColor = computed(() => {
178
183
  }
179
184
 
180
185
  .kline-tooltip__grid .row span:first-child {
181
- color: rgba(0, 0, 0, 0.56);
186
+ color: var(--klc-color-tooltip-text);
187
+ opacity: 0.56;
182
188
  }
183
189
 
184
190
  @supports (anchor-name: --kmap-anchor) and (position-anchor: --kmap-anchor) {
@@ -113,114 +113,15 @@
113
113
  </div>
114
114
  </nav>
115
115
 
116
- <!-- 设置弹窗 -->
117
- <Teleport :to="teleportTarget">
118
- <Transition name="overlay">
119
- <div v-if="showSettings" class="settings-overlay" @click="closeSettings">
120
- <Transition name="modal">
121
- <div class="settings-modal" @click.stop>
122
- <!-- 头部 -->
123
- <div class="settings-header">
124
- <div class="header-left">
125
- <span class="settings-title">图表设置</span>
126
- <span class="settings-subtitle">个性化配置</span>
127
- </div>
128
- <div class="header-right">
129
- <button class="settings-close" @click="closeSettings">
130
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
131
- <path d="M18 6L6 18M6 6l12 12" />
132
- </svg>
133
- </button>
134
- </div>
135
- </div>
136
-
137
- <!-- 体部 -->
138
- <div class="settings-body">
139
- <!-- 主图设置 -->
140
- <template v-if="mainSettings.length > 0">
141
- <div class="settings-section-divider">
142
- <span class="settings-section-label">主图设置</span>
143
- </div>
144
- <template v-for="item in mainSettings" :key="item.key">
145
- <div class="settings-item">
146
- <label class="settings-label">
147
- <span>{{ item.label }}</span>
148
- <template v-if="item.type === 'boolean'">
149
- <input
150
- type="checkbox"
151
- class="settings-checkbox"
152
- v-model="settings[item.key]"
153
- />
154
- </template>
155
- <template v-else-if="item.type === 'select' && item.options">
156
- <select class="settings-select" v-model="settings[item.key]">
157
- <option v-for="opt in item.options" :key="opt.value" :value="opt.value">
158
- {{ opt.label }}
159
- </option>
160
- </select>
161
- </template>
162
- </label>
163
- </div>
164
- </template>
165
- </template>
166
-
167
- <!-- 实验性设置 -->
168
- <template v-if="experimentalSettings.length > 0">
169
- <div class="settings-section-divider">
170
- <span class="settings-section-label">实验性 / 调试设置</span>
171
- </div>
172
- <template v-for="item in experimentalSettings" :key="item.key">
173
- <div class="settings-item experimental">
174
- <label class="settings-label">
175
- <span>{{ item.label }}</span>
176
- <template v-if="item.type === 'boolean'">
177
- <input
178
- type="checkbox"
179
- class="settings-checkbox"
180
- v-model="settings[item.key]"
181
- />
182
- </template>
183
- <template v-else-if="item.type === 'select' && item.options">
184
- <select class="settings-select" v-model="settings[item.key]">
185
- <option v-for="opt in item.options" :key="opt.value" :value="opt.value">
186
- {{ opt.label }}
187
- </option>
188
- </select>
189
- </template>
190
- </label>
191
- </div>
192
- </template>
193
- </template>
194
- </div>
195
-
196
- <!-- 底部 -->
197
- <div class="settings-footer">
198
- <button class="settings-btn reset" @click="resetSettings">
199
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
200
- <path d="M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8" />
201
- <path d="M3 3v5h5" />
202
- </svg>
203
- 重置
204
- </button>
205
- <div class="footer-right">
206
- <button class="settings-btn cancel" @click="closeSettings">取消</button>
207
- <button class="settings-btn confirm" @click="confirmSettings">
208
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5">
209
- <path d="M20 6L9 17l-5-5" />
210
- </svg>
211
- 确定
212
- </button>
213
- </div>
214
- </div>
215
- </div>
216
- </Transition>
217
- </div>
218
- </Transition>
219
- </Teleport>
116
+ <ChartSettingsDialog
117
+ :show="showSettings"
118
+ @close="showSettings = false"
119
+ @confirm="handleConfirmSettings"
120
+ />
220
121
  </template>
221
122
 
222
123
  <script setup lang="ts">
223
- import { ref, computed, onMounted, onUnmounted } from 'vue'
124
+ import { ref, onMounted, onUnmounted } from 'vue'
224
125
  import IconTablerPointer from '~icons/tabler/pointer'
225
126
  import IconTablerChartLine from '~icons/tabler/chart-line'
226
127
  import IconTablerArrowUpRight from '~icons/tabler/arrow-up-right'
@@ -241,10 +142,10 @@ import IconTablerBrackets from '~icons/tabler/brackets'
241
142
  import {
242
143
  DEFAULT_SETTINGS,
243
144
  SETTINGS_STORAGE_KEY,
244
- type SettingItem,
145
+ type ChartSettings,
245
146
  } from '@363045841yyt/klinechart-core/config'
246
- import { useFullscreenTeleportTarget } from '../composables/useFullscreenTeleportTarget'
247
147
  import { setCanvasProfilerEnabled } from '../debug/canvasProfiler'
148
+ import ChartSettingsDialog from './ChartSettingsDialog.vue'
248
149
 
249
150
  export interface ToolDef {
250
151
  id: string
@@ -291,49 +192,39 @@ const emit = defineEmits<{
291
192
  (e: 'toggleFullscreen'): void
292
193
  (e: 'zoomIn'): void
293
194
  (e: 'zoomOut'): void
294
- (e: 'settingsChange', settings: Record<string, boolean | string>): void
195
+ (e: 'settingsChange', settings: ChartSettings): void
295
196
  }>()
296
197
 
297
198
  const selectedToolId = ref('cursor')
298
199
  const openGroupId = ref<string | null>(null)
299
200
  const showSettings = ref(false)
300
201
 
301
- const teleportTarget = useFullscreenTeleportTarget()
302
-
303
- const mainSettings = computed(
304
- () => DEFAULT_SETTINGS.filter((s) => s.group === 'main') as unknown as SettingItem[],
305
- )
306
- const experimentalSettings = computed(
307
- () => DEFAULT_SETTINGS.filter((s) => s.group === 'experimental') as unknown as SettingItem[],
308
- )
309
-
310
- function loadSettings(): Record<string, boolean | string> {
202
+ function loadSettings(): ChartSettings {
311
203
  try {
312
204
  const saved = localStorage.getItem(SETTINGS_STORAGE_KEY)
313
205
  if (saved) {
314
206
  const parsed = JSON.parse(saved)
315
- const result: Record<string, boolean | string> = {}
207
+ const result: ChartSettings = { ...parsed }
316
208
  DEFAULT_SETTINGS.forEach((item) => {
317
209
  result[item.key] = parsed[item.key] ?? item.default
318
210
  })
319
211
  return result
320
212
  }
321
213
  } catch {}
322
- const defaults: Record<string, boolean | string> = {}
214
+ const defaults: ChartSettings = {}
323
215
  DEFAULT_SETTINGS.forEach((item) => {
324
216
  defaults[item.key] = item.default
325
217
  })
326
218
  return defaults
327
219
  }
328
220
 
329
- function saveSettings(settings: Record<string, boolean | string>) {
221
+ function saveSettings(settings: ChartSettings) {
330
222
  try {
331
223
  localStorage.setItem(SETTINGS_STORAGE_KEY, JSON.stringify(settings))
332
224
  } catch {}
333
225
  }
334
226
 
335
- const appliedSettings = ref<Record<string, boolean | string>>(loadSettings())
336
- const settings = ref<Record<string, boolean | string>>({ ...appliedSettings.value })
227
+ const appliedSettings = ref<ChartSettings>(loadSettings())
337
228
 
338
229
  function isActive(tool: ToolDef): boolean {
339
230
  if (selectedToolId.value === tool.id) return true
@@ -370,38 +261,25 @@ function toggleExpand(groupId: string) {
370
261
  }
371
262
 
372
263
  function openSettings() {
373
- settings.value = { ...appliedSettings.value }
374
264
  showSettings.value = true
375
265
  }
376
266
 
377
- function closeSettings() {
378
- showSettings.value = false
267
+ function getCurrentSettings(): ChartSettings {
268
+ return { ...appliedSettings.value }
379
269
  }
380
270
 
381
- function resetSettings() {
382
- const defaults: Record<string, boolean | string> = {}
383
- DEFAULT_SETTINGS.forEach((item) => {
384
- defaults[item.key] = item.default
385
- })
386
- settings.value = defaults
387
- }
271
+ defineExpose({
272
+ getSettings: getCurrentSettings,
273
+ })
388
274
 
389
- function confirmSettings() {
390
- appliedSettings.value = { ...settings.value }
275
+ function handleConfirmSettings(draft: ChartSettings) {
276
+ appliedSettings.value = { ...draft }
391
277
  saveSettings(appliedSettings.value)
392
278
  setCanvasProfilerEnabled(!!appliedSettings.value['enableCanvasProfiler'])
393
279
  emit('settingsChange', { ...appliedSettings.value })
394
- closeSettings()
395
- }
396
-
397
- function getCurrentSettings(): Record<string, boolean | string> {
398
- return { ...appliedSettings.value }
280
+ showSettings.value = false
399
281
  }
400
282
 
401
- defineExpose({
402
- getSettings: getCurrentSettings,
403
- })
404
-
405
283
  function handleClickOutside(e: MouseEvent) {
406
284
  const target = e.target as HTMLElement
407
285
  if (!target.closest('.tool-item')) {
@@ -428,9 +306,9 @@ onUnmounted(() => {
428
306
  align-items: center;
429
307
  gap: 6px;
430
308
  padding: 8px 5px;
431
- border: 1px solid #e5e7eb;
309
+ border: 1px solid var(--klc-color-border-chart);
432
310
  border-radius: 6px;
433
- background: #fafbfc;
311
+ background: var(--klc-color-background);
434
312
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
435
313
  box-sizing: border-box;
436
314
  user-select: none;
@@ -445,7 +323,7 @@ onUnmounted(() => {
445
323
  .left-toolbar__divider {
446
324
  width: 18px;
447
325
  height: 1px;
448
- background: #e5e7eb;
326
+ background: var(--klc-color-border-chart);
449
327
  }
450
328
 
451
329
  /* --- 工具按钮 --- */
@@ -457,7 +335,7 @@ onUnmounted(() => {
457
335
  border: 1px solid transparent;
458
336
  border-radius: 4px;
459
337
  background: transparent;
460
- color: #6b7280;
338
+ color: var(--klc-color-axis-text);
461
339
  cursor: pointer;
462
340
  display: inline-flex;
463
341
  align-items: center;
@@ -469,20 +347,20 @@ onUnmounted(() => {
469
347
  }
470
348
 
471
349
  .left-toolbar__button:hover {
472
- border-color: #d1d5db;
473
- background: #f3f4f6;
474
- color: #374151;
350
+ border-color: var(--klc-color-axis-line);
351
+ background: var(--klc-color-tag-bg-hover);
352
+ color: var(--klc-color-foreground);
475
353
  }
476
354
 
477
355
  .left-toolbar__button.active {
478
- border-color: #9ca3af;
479
- background: #e5e7eb;
480
- color: #1f2937;
356
+ border-color: var(--klc-color-border-chart);
357
+ background: var(--klc-color-grid-major);
358
+ color: var(--klc-color-foreground);
481
359
  }
482
360
 
483
361
  .left-toolbar__button:focus-visible {
484
362
  outline: none;
485
- border-color: #6b7280;
363
+ border-color: var(--klc-color-axis-text);
486
364
  }
487
365
 
488
366
  .tool-icon {
@@ -538,10 +416,10 @@ onUnmounted(() => {
538
416
  gap: 4px;
539
417
  padding: 0 5px;
540
418
  height: 40px;
541
- background: rgba(250, 251, 252, 0.82);
419
+ background: var(--klc-color-tag-bg-white);
542
420
  backdrop-filter: blur(8px);
543
421
  -webkit-backdrop-filter: blur(8px);
544
- border: 1px solid #e5e7eb;
422
+ border: 1px solid var(--klc-color-border-chart);
545
423
  border-radius: 6px;
546
424
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
547
425
  box-sizing: border-box;
@@ -605,240 +483,4 @@ onUnmounted(() => {
605
483
  }
606
484
  }
607
485
 
608
- /* ═══ 设置弹窗样式(参考 IndicatorParams.vue)═══ */
609
- .settings-overlay {
610
- position: fixed;
611
- inset: 0;
612
- background: rgba(0, 0, 0, 0.3);
613
- backdrop-filter: blur(4px);
614
- display: flex;
615
- align-items: center;
616
- justify-content: center;
617
- z-index: 1000;
618
- }
619
-
620
- .settings-modal {
621
- background: #ffffff;
622
- border: 1px solid #e0e0e0;
623
- border-radius: 12px;
624
- box-shadow: 0 8px 40px rgba(0, 0, 0, 0.15);
625
- min-width: 340px;
626
- max-width: 420px;
627
- width: 90vw;
628
- overflow: hidden;
629
- }
630
-
631
- .settings-header {
632
- display: flex;
633
- justify-content: space-between;
634
- align-items: center;
635
- padding: 16px 20px;
636
- background: #f8f8f8;
637
- border-bottom: 1px solid #e8e8e8;
638
- }
639
-
640
- .header-left {
641
- display: flex;
642
- align-items: baseline;
643
- gap: 8px;
644
- }
645
-
646
- .header-right {
647
- display: flex;
648
- align-items: center;
649
- gap: 8px;
650
- }
651
-
652
- .settings-title {
653
- font-size: 14px;
654
- font-weight: 600;
655
- color: #1a1a1a;
656
- letter-spacing: 0.2px;
657
- }
658
-
659
- .settings-subtitle {
660
- font-size: 11px;
661
- color: #999;
662
- }
663
-
664
- .settings-close {
665
- background: #fff;
666
- border: 1px solid #e0e0e0;
667
- border-radius: 6px;
668
- width: 28px;
669
- height: 28px;
670
- display: flex;
671
- align-items: center;
672
- justify-content: center;
673
- cursor: pointer;
674
- color: #888;
675
- transition:
676
- background 0.15s,
677
- color 0.15s,
678
- border-color 0.15s;
679
- padding: 0;
680
- }
681
-
682
- .settings-close:hover {
683
- background: #f0f0f0;
684
- color: #333;
685
- border-color: #ccc;
686
- }
687
-
688
- .settings-close svg {
689
- width: 14px;
690
- height: 14px;
691
- }
692
-
693
- .settings-body {
694
- padding: 16px 20px;
695
- display: flex;
696
- flex-direction: column;
697
- gap: 10px;
698
- }
699
-
700
- .settings-item {
701
- padding: 8px 12px;
702
- border-radius: 8px;
703
- background: #f8f8f8;
704
- border: 1px solid #e8e8e8;
705
- }
706
-
707
- .settings-label {
708
- display: flex;
709
- align-items: center;
710
- justify-content: space-between;
711
- font-size: 13px;
712
- color: #333;
713
- cursor: pointer;
714
- }
715
-
716
- .settings-checkbox {
717
- width: 16px;
718
- height: 16px;
719
- cursor: pointer;
720
- accent-color: #1a1a1a;
721
- }
722
-
723
- .settings-select {
724
- padding: 4px 8px;
725
- border: 1px solid #d0d0d0;
726
- border-radius: 6px;
727
- background: #fff;
728
- color: #333;
729
- font-size: 12px;
730
- cursor: pointer;
731
- outline: none;
732
- min-width: 140px;
733
- }
734
-
735
- .settings-select:hover {
736
- border-color: #9ca3af;
737
- }
738
-
739
- .settings-select:focus {
740
- border-color: #6b7280;
741
- box-shadow: 0 0 0 2px rgba(107, 114, 128, 0.15);
742
- }
743
-
744
- .settings-section-divider {
745
- display: flex;
746
- align-items: center;
747
- gap: 8px;
748
- margin-top: 4px;
749
- }
750
-
751
- .settings-section-divider::before,
752
- .settings-section-divider::after {
753
- content: '';
754
- flex: 1;
755
- border-top: 1px solid #e0e0e0;
756
- }
757
-
758
- .settings-section-label {
759
- font-size: 11px;
760
- color: #999;
761
- white-space: nowrap;
762
- }
763
-
764
- .settings-item.experimental {
765
- border-color: #f0e0d0;
766
- background: #fdf8f3;
767
- }
768
-
769
- .settings-footer {
770
- display: flex;
771
- align-items: center;
772
- justify-content: space-between;
773
- padding: 12px 20px;
774
- background: #f8f8f8;
775
- border-top: 1px solid #e8e8e8;
776
- }
777
-
778
- .footer-right {
779
- display: flex;
780
- gap: 8px;
781
- }
782
-
783
- .settings-btn {
784
- display: flex;
785
- align-items: center;
786
- gap: 5px;
787
- padding: 6px 14px;
788
- border-radius: 7px;
789
- font-size: 13px;
790
- font-weight: 500;
791
- cursor: pointer;
792
- border: 1px solid transparent;
793
- transition: all 0.15s;
794
- line-height: 1.4;
795
- }
796
-
797
- .settings-btn svg {
798
- width: 12px;
799
- height: 12px;
800
- flex-shrink: 0;
801
- }
802
-
803
- .settings-btn.reset {
804
- background: transparent;
805
- border-color: #d0d0d0;
806
- color: #666;
807
- }
808
-
809
- .settings-btn.reset:hover {
810
- border-color: #c0392b;
811
- color: #e74c3c;
812
- background: rgba(231, 76, 60, 0.08);
813
- }
814
-
815
- .settings-btn.cancel {
816
- background: transparent;
817
- border-color: #d0d0d0;
818
- color: #666;
819
- }
820
-
821
- .settings-btn.cancel:hover {
822
- background: #f0f0f0;
823
- color: #333;
824
- border-color: #bbb;
825
- }
826
-
827
- .settings-btn.confirm {
828
- background: #1a1a1a;
829
- border-color: #1a1a1a;
830
- color: #fff;
831
- }
832
-
833
- .settings-btn.confirm:hover {
834
- background: #333;
835
- border-color: #333;
836
- box-shadow: 0 2px 10px rgba(0, 0, 0, 0.15);
837
- transform: translateY(-1px);
838
- }
839
-
840
- .settings-btn.confirm:active {
841
- transform: translateY(0);
842
- box-shadow: none;
843
- }
844
486
  </style>
@@ -19,18 +19,7 @@
19
19
  <script setup lang="ts">
20
20
  import { computed } from 'vue'
21
21
  import type { ComponentPublicInstance } from 'vue'
22
-
23
- interface MarkerEntity {
24
- markerType: string
25
- metadata: Record<string, unknown>
26
- }
27
-
28
- interface CustomMarkerEntity {
29
- date: string
30
- shape: string
31
- label?: { text: string }
32
- metadata: Record<string, unknown>
33
- }
22
+ import type { MarkerEntity, CustomMarkerEntity } from '@363045841yyt/klinechart-core/engine/marker/registry'
34
23
 
35
24
  const MARKER_TYPE_LABELS: Record<string, string> = {
36
25
  support: '支撑位',
@@ -102,10 +91,10 @@ function formatValue(value: unknown): string {
102
91
  max-width: 260px;
103
92
  padding: 10px 12px;
104
93
  border-radius: 8px;
105
- background: rgba(255, 255, 255, 0.92);
106
- border: 1px solid rgba(0, 0, 0, 0.12);
94
+ background: var(--klc-color-tooltip-bg);
95
+ border: 1px solid var(--klc-color-tooltip-border);
107
96
  box-shadow: 0 6px 18px rgba(0, 0, 0, 0.12);
108
- color: rgba(0, 0, 0, 0.78);
97
+ color: var(--klc-color-tooltip-text);
109
98
  font-size: 12px;
110
99
  line-height: 1.4;
111
100
  pointer-events: none;
@@ -133,7 +122,7 @@ function formatValue(value: unknown): string {
133
122
  }
134
123
 
135
124
  .marker-tooltip__content .row span:first-child {
136
- color: rgba(0, 0, 0, 0.56);
125
+ color: color-mix(in srgb, var(--klc-color-tooltip-text) 70%, transparent);
137
126
  }
138
127
 
139
128
  @supports (anchor-name: --kmap-anchor) and (position-anchor: --kmap-anchor) {
@@ -1,3 +1,4 @@
1
+ export { default as ColorPresetPanel } from './ColorPresetPanel.vue'
1
2
  export { default as DrawingStyleToolbar } from './DrawingStyleToolbar.vue'
2
3
  export { default as IndicatorParams } from './IndicatorParams.vue'
3
4
  export { default as IndicatorSelector } from './IndicatorSelector.vue'
@@ -8,11 +8,9 @@ export function provideFullscreenTeleportTarget(targetRef: Ref<HTMLElement | nul
8
8
  }
9
9
 
10
10
  export function useFullscreenTeleportTarget() {
11
- // null = no provider in ancestor tree (degraded scenario)
12
11
  const targetRef = inject(FULLSCREEN_TARGET_KEY, null)
13
12
 
14
13
  return computed<HTMLElement | string>(() => {
15
- // targetRef null → no provider; targetRef.value null → container not mounted yet
16
14
  return targetRef?.value ?? 'body'
17
15
  })
18
16
  }