@363045841yyt/klinechart 0.7.6 → 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.
@@ -0,0 +1,289 @@
1
+ <template>
2
+ <div>
3
+ <div class="color-preset-tools">
4
+ <div class="theme-tabs" role="tablist" aria-label="颜色主题">
5
+ <button
6
+ v-for="option in themeOptions"
7
+ :key="option.value"
8
+ type="button"
9
+ class="theme-tab"
10
+ :class="{ active: editingTheme === option.value }"
11
+ @click="editingTheme = option.value"
12
+ >
13
+ {{ option.label }}
14
+ </button>
15
+ </div>
16
+ <button type="button" class="color-reset-btn" @click="resetCurrentThemeColors">
17
+ 重置颜色
18
+ </button>
19
+ </div>
20
+ <template v-for="group in colorPresetGroups" :key="group.group">
21
+ <div class="color-group-label">{{ group.label }}</div>
22
+ <div class="color-grid">
23
+ <label v-for="item in group.items" :key="item.key" class="color-item">
24
+ <span>{{ item.label }}</span>
25
+ <input
26
+ type="color"
27
+ class="color-input"
28
+ :value="getColorValue(item.key)"
29
+ @input="setColorValue(item.key, ($event.target as HTMLInputElement).value)"
30
+ />
31
+ </label>
32
+ </div>
33
+ </template>
34
+ </div>
35
+ </template>
36
+
37
+ <script setup lang="ts">
38
+ import { ref, computed } from 'vue'
39
+ import {
40
+ COLOR_PRESET_ITEMS,
41
+ darkTheme,
42
+ lightTheme,
43
+ normalizeColorPresetSettings,
44
+ type ColorPresetKey,
45
+ type ColorPresetThemeName,
46
+ type ColorPresetSettings,
47
+ } from '@363045841yyt/klinechart-core'
48
+
49
+ const props = defineProps<{
50
+ colorPresetSettings: ColorPresetSettings | undefined
51
+ }>()
52
+
53
+ const emit = defineEmits<{
54
+ (e: 'update:colorPresetSettings', value: ColorPresetSettings): void
55
+ }>()
56
+
57
+ const themeOptions: readonly { value: ColorPresetThemeName; label: string }[] = [
58
+ { value: 'light', label: '浅色' },
59
+ { value: 'dark', label: '深色' },
60
+ ]
61
+
62
+ const colorGroupLabels = {
63
+ canvas: '画布',
64
+ candle: 'K线 / 成交量',
65
+ axis: '坐标轴',
66
+ interaction: '交互 / 标记',
67
+ } as const
68
+
69
+ const colorPresetGroups = computed(() => {
70
+ return (Object.keys(colorGroupLabels) as Array<keyof typeof colorGroupLabels>)
71
+ .map((group) => ({
72
+ group,
73
+ label: colorGroupLabels[group],
74
+ items: COLOR_PRESET_ITEMS.filter((item) => item.group === group),
75
+ }))
76
+ .filter((group) => group.items.length > 0)
77
+ })
78
+
79
+ const editingTheme = ref<ColorPresetThemeName>('light')
80
+
81
+ function getThemeDefaultColor(themeName: ColorPresetThemeName, key: ColorPresetKey): string {
82
+ const theme = themeName === 'dark' ? darkTheme : lightTheme
83
+ return theme.colors[key]
84
+ }
85
+
86
+ function getColorValue(key: ColorPresetKey): string {
87
+ const colorSettings = normalizeColorPresetSettings(props.colorPresetSettings)
88
+ return colorSettings[editingTheme.value]?.[key] ?? getThemeDefaultColor(editingTheme.value, key)
89
+ }
90
+
91
+ function setColorValue(key: ColorPresetKey, value: string): void {
92
+ const colorSettings = normalizeColorPresetSettings(props.colorPresetSettings)
93
+ emit('update:colorPresetSettings', {
94
+ ...colorSettings,
95
+ [editingTheme.value]: {
96
+ ...colorSettings[editingTheme.value],
97
+ [key]: value,
98
+ },
99
+ })
100
+ }
101
+
102
+ function resetCurrentThemeColors(): void {
103
+ const colorSettings = normalizeColorPresetSettings(props.colorPresetSettings)
104
+ const nextColorSettings = { ...colorSettings }
105
+ delete nextColorSettings[editingTheme.value]
106
+ emit('update:colorPresetSettings', nextColorSettings)
107
+ }
108
+ </script>
109
+
110
+ <style scoped>
111
+ /* ── 工具栏 ── */
112
+ .color-preset-tools {
113
+ display: grid;
114
+ grid-template-columns: 1fr auto;
115
+ align-items: center;
116
+ gap: 8px;
117
+ margin-bottom: 4px;
118
+ }
119
+
120
+ /* ── 主题切换 ── */
121
+ .theme-tabs {
122
+ display: grid;
123
+ grid-template-columns: 1fr 1fr;
124
+ gap: 3px;
125
+ padding: 3px;
126
+ border: 1px solid var(--klc-color-border-button);
127
+ border-radius: 8px;
128
+ background: var(--klc-color-grid-minor);
129
+ }
130
+
131
+ .theme-tab {
132
+ height: 28px;
133
+ border: none;
134
+ border-radius: 6px;
135
+ background: transparent;
136
+ color: var(--klc-color-axis-text);
137
+ font-size: 12px;
138
+ font-weight: 500;
139
+ cursor: pointer;
140
+ transition:
141
+ background 0.18s ease,
142
+ color 0.18s ease,
143
+ box-shadow 0.18s ease;
144
+ }
145
+
146
+ .theme-tab:not(.active):hover {
147
+ color: var(--klc-color-foreground);
148
+ background: color-mix(in srgb, var(--klc-color-tag-bg-white) 60%, transparent);
149
+ }
150
+
151
+ .theme-tab.active {
152
+ background: var(--klc-color-tag-bg-white);
153
+ color: var(--klc-color-foreground);
154
+ font-weight: 600;
155
+ box-shadow: 0 1px 4px rgba(0, 0, 0, 0.1);
156
+ }
157
+
158
+ /* ── 重置按钮 ── */
159
+ .color-reset-btn {
160
+ height: 36px;
161
+ padding: 0 14px;
162
+ border: 1px solid var(--klc-color-axis-line);
163
+ border-radius: 8px;
164
+ background: var(--klc-color-tag-bg-white);
165
+ color: var(--klc-color-axis-text);
166
+ font-size: 12px;
167
+ font-weight: 500;
168
+ white-space: nowrap;
169
+ cursor: pointer;
170
+ transition:
171
+ background 0.18s ease,
172
+ border-color 0.18s ease,
173
+ color 0.18s ease,
174
+ box-shadow 0.18s ease;
175
+ }
176
+
177
+ .color-reset-btn:hover {
178
+ border-color: var(--klc-color-axis-text);
179
+ background: var(--klc-color-background);
180
+ color: var(--klc-color-foreground);
181
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
182
+ }
183
+
184
+ .color-reset-btn:active {
185
+ background: var(--klc-color-tag-bg-hover);
186
+ box-shadow: none;
187
+ }
188
+
189
+ /* ── 分组标签 ── */
190
+ .color-group-label {
191
+ margin: 6px 0 6px;
192
+ color: var(--klc-color-axis-text);
193
+ font-size: 12px;
194
+ font-weight: 600;
195
+ line-height: 1.3;
196
+ }
197
+
198
+ /* ── 颜色网格 ── */
199
+ .color-grid {
200
+ display: grid;
201
+ grid-template-columns: repeat(2, minmax(0, 1fr));
202
+ gap: 6px;
203
+ }
204
+
205
+ /* ── 颜色条目 ── */
206
+ .color-item {
207
+ display: flex;
208
+ align-items: center;
209
+ justify-content: space-between;
210
+ gap: 8px;
211
+ min-height: 36px;
212
+ padding: 6px 10px;
213
+ border: 1px solid var(--klc-color-grid-major);
214
+ border-radius: 8px;
215
+ background: var(--klc-color-background);
216
+ color: var(--klc-color-foreground);
217
+ font-size: 12px;
218
+ line-height: 1.3;
219
+ cursor: pointer;
220
+ transition:
221
+ border-color 0.18s ease,
222
+ background 0.18s ease,
223
+ box-shadow 0.18s ease;
224
+ }
225
+
226
+ .color-item:hover {
227
+ border-color: var(--klc-color-axis-line);
228
+ background: var(--klc-color-tag-bg-hover);
229
+ box-shadow: 0 1px 4px color-mix(in srgb, var(--klc-color-foreground) 6%, transparent);
230
+ }
231
+
232
+ .color-item span {
233
+ min-width: 0;
234
+ overflow: hidden;
235
+ text-overflow: ellipsis;
236
+ white-space: nowrap;
237
+ user-select: none;
238
+ }
239
+
240
+ /* ── 颜色输入 ── */
241
+ .color-input {
242
+ flex: 0 0 auto;
243
+ width: 26px;
244
+ height: 26px;
245
+ padding: 0;
246
+ border: 1px solid var(--klc-color-axis-line);
247
+ border-radius: 6px;
248
+ background: transparent;
249
+ cursor: pointer;
250
+ transition:
251
+ border-color 0.18s ease,
252
+ box-shadow 0.18s ease;
253
+ }
254
+
255
+ .color-input:hover {
256
+ border-color: var(--klc-color-axis-text);
257
+ box-shadow: 0 0 0 2px color-mix(in srgb, var(--klc-color-foreground) 6%, transparent);
258
+ }
259
+
260
+ .color-input:focus-visible {
261
+ outline: none;
262
+ border-color: var(--klc-color-axis-text);
263
+ box-shadow: 0 0 0 2px color-mix(in srgb, var(--klc-color-foreground) 10%, transparent);
264
+ }
265
+
266
+ .color-input::-webkit-color-swatch-wrapper {
267
+ padding: 2px;
268
+ }
269
+
270
+ .color-input::-webkit-color-swatch {
271
+ border: none;
272
+ border-radius: 4px;
273
+ }
274
+
275
+ /* ── 响应式 ── */
276
+ @media (max-width: 480px) {
277
+ .color-preset-tools {
278
+ grid-template-columns: 1fr;
279
+ }
280
+
281
+ .color-reset-btn {
282
+ width: 100%;
283
+ }
284
+
285
+ .color-grid {
286
+ grid-template-columns: 1fr;
287
+ }
288
+ }
289
+ </style>
@@ -95,10 +95,10 @@ function onLineStyleChange(style: 'solid' | 'dashed' | 'dotted') {
95
95
  gap: 6px;
96
96
  padding: 4px 8px;
97
97
  height: 32px;
98
- background: rgba(250, 251, 252, 0.88);
98
+ background: color-mix(in srgb, var(--klc-color-tag-bg-white) 88%, transparent);
99
99
  backdrop-filter: blur(8px);
100
100
  -webkit-backdrop-filter: blur(8px);
101
- border: 1px solid #e5e7eb;
101
+ border: 1px solid var(--klc-color-border-button);
102
102
  border-radius: 6px;
103
103
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
104
104
  z-index: 100;
@@ -122,7 +122,7 @@ function onLineStyleChange(style: 'solid' | 'dashed' | 'dotted') {
122
122
  display: block;
123
123
  width: 100%;
124
124
  height: 100%;
125
- border: 1px solid #d1d5db;
125
+ border: 1px solid var(--klc-color-axis-line);
126
126
  border-radius: 4px;
127
127
  cursor: pointer;
128
128
  }
@@ -139,17 +139,17 @@ function onLineStyleChange(style: 'solid' | 'dashed' | 'dotted') {
139
139
  .toolbar-select {
140
140
  height: 24px;
141
141
  padding: 0 4px;
142
- border: 1px solid #d1d5db;
142
+ border: 1px solid var(--klc-color-axis-line);
143
143
  border-radius: 4px;
144
- background: #fff;
145
- color: #374151;
144
+ background: var(--klc-color-tag-bg-white);
145
+ color: var(--klc-color-foreground);
146
146
  font-size: 12px;
147
147
  cursor: pointer;
148
148
  outline: none;
149
149
  }
150
150
 
151
151
  .toolbar-select:hover {
152
- border-color: #9ca3af;
152
+ border-color: var(--klc-color-axis-text);
153
153
  }
154
154
 
155
155
  .toolbar-btn {
@@ -162,15 +162,15 @@ function onLineStyleChange(style: 'solid' | 'dashed' | 'dotted') {
162
162
  border: 1px solid transparent;
163
163
  border-radius: 4px;
164
164
  background: transparent;
165
- color: #6b7280;
165
+ color: var(--klc-color-axis-text);
166
166
  cursor: pointer;
167
167
  transition: border-color 0.15s ease, background 0.15s ease, color 0.15s ease;
168
168
  }
169
169
 
170
170
  .toolbar-btn:hover {
171
- border-color: #d1d5db;
172
- background: #f3f4f6;
173
- color: #374151;
171
+ border-color: var(--klc-color-axis-line);
172
+ background: var(--klc-color-grid-minor);
173
+ color: var(--klc-color-foreground);
174
174
  }
175
175
 
176
176
  .delete-btn:hover {
@@ -206,8 +206,8 @@ function onConfirm() {
206
206
 
207
207
  /* ── 弹窗 ── */
208
208
  .indicator-params {
209
- background: #ffffff;
210
- border: 1px solid #e0e0e0;
209
+ background: var(--klc-color-tag-bg-white);
210
+ border: 1px solid var(--klc-color-border-button);
211
211
  border-radius: 12px;
212
212
  box-shadow: 0 8px 40px rgba(0, 0, 0, 0.15);
213
213
  min-width: 340px;
@@ -222,8 +222,8 @@ function onConfirm() {
222
222
  justify-content: space-between;
223
223
  align-items: center;
224
224
  padding: 16px 20px;
225
- background: #f8f8f8;
226
- border-bottom: 1px solid #e8e8e8;
225
+ background: var(--klc-color-background);
226
+ border-bottom: 1px solid var(--klc-color-grid-major);
227
227
  }
228
228
 
229
229
  .header-left {
@@ -241,18 +241,18 @@ function onConfirm() {
241
241
  .params-title {
242
242
  font-size: 14px;
243
243
  font-weight: 600;
244
- color: #1a1a1a;
244
+ color: var(--klc-color-foreground);
245
245
  letter-spacing: 0.2px;
246
246
  }
247
247
 
248
248
  .params-subtitle {
249
249
  font-size: 11px;
250
- color: #999;
250
+ color: var(--klc-color-axis-text);
251
251
  }
252
252
 
253
253
  .toggle-desc-btn {
254
- background: #fff;
255
- border: 1px solid #e0e0e0;
254
+ background: var(--klc-color-tag-bg-white);
255
+ border: 1px solid var(--klc-color-border-button);
256
256
  border-radius: 6px;
257
257
  width: 28px;
258
258
  height: 28px;
@@ -260,21 +260,21 @@ function onConfirm() {
260
260
  align-items: center;
261
261
  justify-content: center;
262
262
  cursor: pointer;
263
- color: #888;
263
+ color: var(--klc-color-axis-text);
264
264
  transition: all 0.2s;
265
265
  padding: 0;
266
266
  }
267
267
 
268
268
  .toggle-desc-btn:hover {
269
- background: #f0f0f0;
270
- color: #555;
271
- border-color: #ccc;
269
+ background: var(--klc-color-tag-bg-hover);
270
+ color: var(--klc-color-foreground);
271
+ border-color: var(--klc-color-axis-line);
272
272
  }
273
273
 
274
274
  .toggle-desc-btn.active {
275
- background: #1a1a1a;
276
- border-color: #1a1a1a;
277
- color: #fff;
275
+ background: var(--klc-color-foreground);
276
+ border-color: var(--klc-color-foreground);
277
+ color: var(--klc-color-background);
278
278
  }
279
279
 
280
280
  .toggle-desc-btn svg {
@@ -283,8 +283,8 @@ function onConfirm() {
283
283
  }
284
284
 
285
285
  .params-close {
286
- background: #fff;
287
- border: 1px solid #e0e0e0;
286
+ background: var(--klc-color-tag-bg-white);
287
+ border: 1px solid var(--klc-color-border-button);
288
288
  border-radius: 6px;
289
289
  width: 28px;
290
290
  height: 28px;
@@ -292,15 +292,15 @@ function onConfirm() {
292
292
  align-items: center;
293
293
  justify-content: center;
294
294
  cursor: pointer;
295
- color: #888;
295
+ color: var(--klc-color-axis-text);
296
296
  transition: background 0.15s, color 0.15s, border-color 0.15s;
297
297
  padding: 0;
298
298
  }
299
299
 
300
300
  .params-close:hover {
301
- background: #f0f0f0;
302
- color: #333;
303
- border-color: #ccc;
301
+ background: var(--klc-color-tag-bg-hover);
302
+ color: var(--klc-color-foreground);
303
+ border-color: var(--klc-color-axis-line);
304
304
  }
305
305
 
306
306
  .params-close svg {
@@ -311,15 +311,15 @@ function onConfirm() {
311
311
  /* ── 指标描述 ── */
312
312
  .indicator-description {
313
313
  padding: 12px 20px;
314
- background: #f0f7ff;
315
- border-bottom: 1px solid #d6e8f5;
314
+ background: color-mix(in srgb, var(--klc-color-alert-active) 10%, var(--klc-color-background));
315
+ border-bottom: 1px solid color-mix(in srgb, var(--klc-color-alert-active) 20%, transparent);
316
316
  }
317
317
 
318
318
  .indicator-description p {
319
319
  margin: 0;
320
320
  font-size: 12px;
321
321
  line-height: 1.6;
322
- color: #2c5282;
322
+ color: var(--klc-color-alert-active);
323
323
  }
324
324
 
325
325
  /* ── 体部 ── */
@@ -333,13 +333,13 @@ function onConfirm() {
333
333
  .param-item {
334
334
  padding: 10px 14px;
335
335
  border-radius: 8px;
336
- background: #f8f8f8;
337
- border: 1px solid #e8e8e8;
336
+ background: var(--klc-color-background);
337
+ border: 1px solid var(--klc-color-grid-major);
338
338
  transition: border-color 0.2s;
339
339
  }
340
340
 
341
341
  .param-item:has(.param-input:focus) {
342
- border-color: #bbb;
342
+ border-color: var(--klc-color-axis-text);
343
343
  }
344
344
 
345
345
  .param-item.has-desc {
@@ -362,22 +362,22 @@ function onConfirm() {
362
362
  .param-label-text {
363
363
  font-size: 13px;
364
364
  font-weight: 500;
365
- color: #333;
365
+ color: var(--klc-color-foreground);
366
366
  }
367
367
 
368
368
  .param-range {
369
369
  font-size: 11px;
370
- color: #999;
370
+ color: var(--klc-color-axis-text);
371
371
  }
372
372
 
373
373
  /* ── 参数描述 ── */
374
374
  .param-description {
375
375
  margin-top: 8px;
376
376
  padding-top: 8px;
377
- border-top: 1px dashed #e0e0e0;
377
+ border-top: 1px dashed var(--klc-color-border-button);
378
378
  font-size: 11px;
379
379
  line-height: 1.5;
380
- color: #666;
380
+ color: var(--klc-color-axis-text);
381
381
  }
382
382
 
383
383
  /* ── 步进输入框 ── */
@@ -385,25 +385,25 @@ function onConfirm() {
385
385
  display: flex;
386
386
  align-items: stretch;
387
387
  height: 32px;
388
- border: 1px solid #d0d0d0;
388
+ border: 1px solid var(--klc-color-axis-line);
389
389
  border-radius: 7px;
390
390
  overflow: hidden;
391
- background: #fff;
391
+ background: var(--klc-color-tag-bg-white);
392
392
  transition: border-color 0.2s;
393
393
  }
394
394
 
395
395
  .input-wrapper:focus-within {
396
- border-color: #999;
396
+ border-color: var(--klc-color-axis-text);
397
397
  }
398
398
 
399
399
  .stepper-btn {
400
400
  width: 28px;
401
- background: #f0f0f0;
401
+ background: var(--klc-color-grid-minor);
402
402
  border: none;
403
403
  cursor: pointer;
404
404
  font-size: 15px;
405
405
  font-weight: 400;
406
- color: #666;
406
+ color: var(--klc-color-axis-text);
407
407
  display: flex;
408
408
  align-items: center;
409
409
  justify-content: center;
@@ -413,24 +413,24 @@ function onConfirm() {
413
413
  }
414
414
 
415
415
  .stepper-btn:hover:not(:disabled) {
416
- background: #e0e0e0;
417
- color: #333;
416
+ background: var(--klc-color-border-button);
417
+ color: var(--klc-color-foreground);
418
418
  }
419
419
 
420
420
  .stepper-btn:disabled {
421
- color: #ccc;
421
+ color: var(--klc-color-axis-line);
422
422
  cursor: not-allowed;
423
423
  }
424
424
 
425
425
  .param-input {
426
426
  width: 60px;
427
427
  border: none;
428
- border-left: 1px solid #e8e8e8;
429
- border-right: 1px solid #e8e8e8;
428
+ border-left: 1px solid var(--klc-color-grid-major);
429
+ border-right: 1px solid var(--klc-color-grid-major);
430
430
  font-size: 13px;
431
431
  font-weight: 600;
432
432
  text-align: center;
433
- color: #1a1a1a;
433
+ color: var(--klc-color-foreground);
434
434
  background: transparent;
435
435
  -moz-appearance: textfield;
436
436
  appearance: textfield;
@@ -451,8 +451,8 @@ function onConfirm() {
451
451
  align-items: center;
452
452
  justify-content: space-between;
453
453
  padding: 12px 20px;
454
- background: #f8f8f8;
455
- border-top: 1px solid #e8e8e8;
454
+ background: var(--klc-color-background);
455
+ border-top: 1px solid var(--klc-color-grid-major);
456
456
  }
457
457
 
458
458
  .footer-right {
@@ -483,8 +483,8 @@ function onConfirm() {
483
483
  /* 重置 */
484
484
  .params-btn.reset {
485
485
  background: transparent;
486
- border-color: #d0d0d0;
487
- color: #666;
486
+ border-color: var(--klc-color-axis-line);
487
+ color: var(--klc-color-axis-text);
488
488
  }
489
489
 
490
490
  .params-btn.reset:hover {
@@ -496,26 +496,26 @@ function onConfirm() {
496
496
  /* 取消 */
497
497
  .params-btn.cancel {
498
498
  background: transparent;
499
- border-color: #d0d0d0;
500
- color: #666;
499
+ border-color: var(--klc-color-axis-line);
500
+ color: var(--klc-color-axis-text);
501
501
  }
502
502
 
503
503
  .params-btn.cancel:hover {
504
- background: #f0f0f0;
505
- color: #333;
506
- border-color: #bbb;
504
+ background: var(--klc-color-tag-bg-hover);
505
+ color: var(--klc-color-foreground);
506
+ border-color: var(--klc-color-axis-text);
507
507
  }
508
508
 
509
509
  /* 确定 */
510
510
  .params-btn.confirm {
511
- background: #1a1a1a;
512
- border-color: #1a1a1a;
513
- color: #fff;
511
+ background: var(--klc-color-foreground);
512
+ border-color: var(--klc-color-foreground);
513
+ color: var(--klc-color-background);
514
514
  }
515
515
 
516
516
  .params-btn.confirm:hover {
517
- background: #333;
518
- border-color: #333;
517
+ background: var(--klc-color-foreground);
518
+ border-color: var(--klc-color-foreground);
519
519
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.15);
520
520
  transform: translateY(-1px);
521
521
  }