@363045841yyt/klinechart 0.8.5 → 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.
- package/README.md +6 -1
- package/dist/components/BaseModal.vue.d.ts +54 -0
- package/dist/components/BaseModal.vue.d.ts.map +1 -0
- package/dist/components/BatchStockDialog.vue.d.ts.map +1 -1
- package/dist/components/ChartSettingsDialog.vue.d.ts.map +1 -1
- package/dist/components/ColorPresetPanel.vue.d.ts +4 -1
- package/dist/components/ColorPresetPanel.vue.d.ts.map +1 -1
- package/dist/components/DrawingStyleToolbar.vue.d.ts.map +1 -1
- package/dist/components/ExportProgressDialog.vue.d.ts.map +1 -1
- package/dist/components/IndicatorParams.vue.d.ts.map +1 -1
- package/dist/components/IndicatorSelector.vue.d.ts.map +1 -1
- package/dist/components/KLineChart.vue.d.ts.map +1 -1
- package/dist/components/RangeSelectionExport.vue.d.ts +23 -0
- package/dist/components/RangeSelectionExport.vue.d.ts.map +1 -0
- package/dist/components/common/CanvasToolbar.vue.d.ts +14 -0
- package/dist/components/common/CanvasToolbar.vue.d.ts.map +1 -0
- package/dist/components/common/CanvasToolbarStack.vue.d.ts +14 -0
- package/dist/components/common/CanvasToolbarStack.vue.d.ts.map +1 -0
- package/dist/composables/chart/useRangeSelection.d.ts +1 -0
- package/dist/composables/chart/useRangeSelection.d.ts.map +1 -1
- package/dist/composables/useTeleportedPopup.d.ts.map +1 -1
- package/dist/index.cjs +6 -6
- package/dist/index.css +1 -1
- package/dist/index.js +1293 -1215
- package/dist/web-component.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/BaseModal.vue +292 -0
- package/src/components/BatchStockDialog.vue +15 -180
- package/src/components/ChartSettingsDialog.vue +248 -405
- package/src/components/ColorPresetPanel.vue +58 -106
- package/src/components/CompareSymbolSelector.vue +2 -2
- package/src/components/DrawingStyleToolbar.vue +33 -72
- package/src/components/ExportProgressDialog.vue +25 -133
- package/src/components/IndicatorParams.vue +194 -321
- package/src/components/IndicatorSelector.vue +188 -405
- package/src/components/KLineChart.vue +34 -138
- package/src/components/LeftToolbar.vue +1 -1
- package/src/components/RangeSelectionExport.vue +117 -0
- package/src/components/SymbolSelector.vue +2 -2
- package/src/components/common/CanvasToolbar.vue +70 -0
- package/src/components/common/CanvasToolbarStack.vue +32 -0
- package/src/composables/chart/useRangeSelection.ts +7 -0
- package/src/composables/useTeleportedPopup.ts +15 -2
|
@@ -60,59 +60,25 @@
|
|
|
60
60
|
<div class="canvas-layer" ref="canvasLayerRef">
|
|
61
61
|
<canvas class="x-axis-canvas" ref="xAxisCanvasRef"></canvas>
|
|
62
62
|
|
|
63
|
-
<
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
@pointerup.stop
|
|
75
|
-
>
|
|
76
|
-
<input
|
|
77
|
-
class="range-selection-export__label"
|
|
78
|
-
v-model="customStartDate"
|
|
79
|
-
:placeholder="rangeSelectionStartLabel"
|
|
63
|
+
<CanvasToolbarStack>
|
|
64
|
+
<RangeSelectionExport
|
|
65
|
+
v-if="rangeSelectionReady"
|
|
66
|
+
v-model:start-date="customStartDate"
|
|
67
|
+
v-model:end-date="customEndDate"
|
|
68
|
+
:start-label="rangeSelectionStartLabel"
|
|
69
|
+
:end-label="rangeSelectionEndLabel"
|
|
70
|
+
:count="rangeSelectionCount"
|
|
71
|
+
@export="exportRangeToCsv"
|
|
72
|
+
@clear="clearRangeSelection"
|
|
73
|
+
@batch-setting="showBatchStockDialog = true"
|
|
80
74
|
/>
|
|
81
|
-
<
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
75
|
+
<DrawingStyleToolbar
|
|
76
|
+
v-if="selectedDrawing"
|
|
77
|
+
:drawing="selectedDrawing"
|
|
78
|
+
@update-style="onUpdateDrawingStyle"
|
|
79
|
+
@delete="onDeleteDrawing"
|
|
86
80
|
/>
|
|
87
|
-
|
|
88
|
-
type="button"
|
|
89
|
-
class="toolbar-btn"
|
|
90
|
-
title="批量设置"
|
|
91
|
-
@click.stop="showBatchStockDialog = true"
|
|
92
|
-
>
|
|
93
|
-
批量设置
|
|
94
|
-
</button>
|
|
95
|
-
<button
|
|
96
|
-
type="button"
|
|
97
|
-
class="toolbar-btn"
|
|
98
|
-
title="导出"
|
|
99
|
-
@click.stop="exportRangeToCsv"
|
|
100
|
-
>
|
|
101
|
-
导出
|
|
102
|
-
</button>
|
|
103
|
-
<button
|
|
104
|
-
type="button"
|
|
105
|
-
class="toolbar-btn delete-btn"
|
|
106
|
-
title="删除选区"
|
|
107
|
-
@click.stop="clearRangeSelection"
|
|
108
|
-
>
|
|
109
|
-
<svg class="delete-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
|
|
110
|
-
<path d="M3 6h18" />
|
|
111
|
-
<path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6" />
|
|
112
|
-
<path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2" />
|
|
113
|
-
</svg>
|
|
114
|
-
</button>
|
|
115
|
-
</div>
|
|
81
|
+
</CanvasToolbarStack>
|
|
116
82
|
</div>
|
|
117
83
|
<div
|
|
118
84
|
v-if="rangeSelectionOverlayStyle"
|
|
@@ -140,7 +106,7 @@
|
|
|
140
106
|
</div>
|
|
141
107
|
<Teleport v-if="tooltipLayerRef" :to="tooltipLayerRef">
|
|
142
108
|
<div
|
|
143
|
-
v-if="hovered"
|
|
109
|
+
v-if="hovered && !isMobile"
|
|
144
110
|
class="tooltip-anchor kline-tooltip-anchor"
|
|
145
111
|
:class="{ 'use-anchor': useAnchorPositioning }"
|
|
146
112
|
:style="klineTooltipAnchorStyle"
|
|
@@ -152,7 +118,7 @@
|
|
|
152
118
|
:style="markerTooltipAnchorStyle"
|
|
153
119
|
></div>
|
|
154
120
|
<KLineTooltip
|
|
155
|
-
v-if="hovered"
|
|
121
|
+
v-if="hovered && !isMobile"
|
|
156
122
|
:k="hovered"
|
|
157
123
|
:index="hoveredIndex"
|
|
158
124
|
:data="chartData"
|
|
@@ -213,6 +179,8 @@ import KLineTooltip from './KLineTooltip.vue'
|
|
|
213
179
|
import MarkerTooltip from './MarkerTooltip.vue'
|
|
214
180
|
import IndicatorSelector from './IndicatorSelector.vue'
|
|
215
181
|
import DrawingStyleToolbar from './DrawingStyleToolbar.vue'
|
|
182
|
+
import RangeSelectionExport from './RangeSelectionExport.vue'
|
|
183
|
+
import CanvasToolbarStack from './common/CanvasToolbarStack.vue'
|
|
216
184
|
import { provideFullscreenTeleportTarget } from '../composables/useFullscreenTeleportTarget'
|
|
217
185
|
import {
|
|
218
186
|
createChartController,
|
|
@@ -449,6 +417,7 @@ const {
|
|
|
449
417
|
isRangeSelectActive,
|
|
450
418
|
rangeSelectionReady,
|
|
451
419
|
rangeSelectionBounds,
|
|
420
|
+
rangeSelectionCount,
|
|
452
421
|
rangeSelectionStartLabel,
|
|
453
422
|
rangeSelectionEndLabel,
|
|
454
423
|
rangeSelectionOverlayStyle,
|
|
@@ -567,6 +536,7 @@ const isResizingPane = computed(() => interactionState.value.isResizingPaneBound
|
|
|
567
536
|
const isHoveringPaneSeparator = computed(() => interactionState.value.isHoveringPaneBoundary)
|
|
568
537
|
const hoveredPaneBoundaryId = computed(() => interactionState.value.hoveredPaneBoundaryId)
|
|
569
538
|
const isHoveringRightAxis = computed(() => interactionState.value.isHoveringRightAxis)
|
|
539
|
+
const isMobile = window.matchMedia('(pointer: coarse)').matches
|
|
570
540
|
const hoveredIdx = computed(() => interactionState.value.hoveredIndex)
|
|
571
541
|
const crosshairIdx = computed(() => interactionState.value.crosshairIndex)
|
|
572
542
|
|
|
@@ -1146,93 +1116,12 @@ watch(
|
|
|
1146
1116
|
z-index: 101;
|
|
1147
1117
|
}
|
|
1148
1118
|
|
|
1149
|
-
.range-selection-handle--left {
|
|
1150
|
-
|
|
1151
|
-
.range-selection-handle--right { right: -4px; }
|
|
1152
|
-
|
|
1153
|
-
.range-selection-export {
|
|
1154
|
-
position: absolute;
|
|
1155
|
-
left: 50%;
|
|
1156
|
-
top: 8px;
|
|
1157
|
-
transform: translateX(-50%);
|
|
1158
|
-
display: flex;
|
|
1159
|
-
align-items: center;
|
|
1160
|
-
gap: 6px;
|
|
1161
|
-
padding: 4px 8px;
|
|
1162
|
-
height: 32px;
|
|
1163
|
-
background: color-mix(in srgb, var(--klc-color-tag-bg-white) 88%, transparent);
|
|
1164
|
-
backdrop-filter: blur(8px);
|
|
1165
|
-
-webkit-backdrop-filter: blur(8px);
|
|
1166
|
-
border: 1px solid var(--klc-color-border-button);
|
|
1167
|
-
border-radius: 6px;
|
|
1168
|
-
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
|
|
1169
|
-
z-index: 100;
|
|
1170
|
-
user-select: none;
|
|
1171
|
-
pointer-events: auto;
|
|
1172
|
-
}
|
|
1173
|
-
|
|
1174
|
-
.range-selection-export .toolbar-btn {
|
|
1175
|
-
display: inline-flex;
|
|
1176
|
-
align-items: center;
|
|
1177
|
-
justify-content: center;
|
|
1178
|
-
height: 24px;
|
|
1179
|
-
padding: 0 8px;
|
|
1180
|
-
border: 1px solid var(--klc-color-border-button);
|
|
1181
|
-
border-radius: 4px;
|
|
1182
|
-
background: transparent;
|
|
1183
|
-
color: var(--klc-color-axis-text);
|
|
1184
|
-
font-size: 12px;
|
|
1185
|
-
cursor: pointer;
|
|
1186
|
-
transition:
|
|
1187
|
-
border-color 0.15s ease,
|
|
1188
|
-
background 0.15s ease,
|
|
1189
|
-
color 0.15s ease;
|
|
1190
|
-
white-space: nowrap;
|
|
1119
|
+
.range-selection-handle--left {
|
|
1120
|
+
left: -4px;
|
|
1191
1121
|
}
|
|
1192
1122
|
|
|
1193
|
-
.range-selection-
|
|
1194
|
-
|
|
1195
|
-
background: var(--klc-color-grid-minor);
|
|
1196
|
-
color: var(--klc-color-foreground);
|
|
1197
|
-
}
|
|
1198
|
-
|
|
1199
|
-
.range-selection-export .toolbar-btn.delete-btn {
|
|
1200
|
-
padding: 0;
|
|
1201
|
-
width: 24px;
|
|
1202
|
-
border-color: transparent;
|
|
1203
|
-
}
|
|
1204
|
-
|
|
1205
|
-
.range-selection-export .toolbar-btn.delete-btn:hover {
|
|
1206
|
-
color: #dc2626;
|
|
1207
|
-
border-color: #fca5a5;
|
|
1208
|
-
background: #fef2f2;
|
|
1209
|
-
}
|
|
1210
|
-
|
|
1211
|
-
.range-selection-export .delete-icon {
|
|
1212
|
-
width: 14px;
|
|
1213
|
-
height: 14px;
|
|
1214
|
-
}
|
|
1215
|
-
|
|
1216
|
-
.range-selection-export__label {
|
|
1217
|
-
color: var(--klc-color-axis-text);
|
|
1218
|
-
font-size: 11px;
|
|
1219
|
-
white-space: nowrap;
|
|
1220
|
-
border: 1px solid var(--klc-color-border-button);
|
|
1221
|
-
background: none;
|
|
1222
|
-
outline: none;
|
|
1223
|
-
padding: 1px 4px;
|
|
1224
|
-
width: 80px;
|
|
1225
|
-
height: 24px;
|
|
1226
|
-
box-sizing: border-box;
|
|
1227
|
-
font-family: inherit;
|
|
1228
|
-
border-radius: 3px;
|
|
1229
|
-
text-align: center;
|
|
1230
|
-
}
|
|
1231
|
-
|
|
1232
|
-
.range-selection-export__sep {
|
|
1233
|
-
color: var(--klc-color-axis-text);
|
|
1234
|
-
font-size: 11px;
|
|
1235
|
-
user-select: none;
|
|
1123
|
+
.range-selection-handle--right {
|
|
1124
|
+
right: -4px;
|
|
1236
1125
|
}
|
|
1237
1126
|
|
|
1238
1127
|
.canvas-layer {
|
|
@@ -1274,6 +1163,7 @@ watch(
|
|
|
1274
1163
|
gap: 4px;
|
|
1275
1164
|
}
|
|
1276
1165
|
}
|
|
1166
|
+
|
|
1277
1167
|
</style>
|
|
1278
1168
|
|
|
1279
1169
|
<style>
|
|
@@ -1302,3 +1192,9 @@ watch(
|
|
|
1302
1192
|
z-index: 15;
|
|
1303
1193
|
}
|
|
1304
1194
|
</style>
|
|
1195
|
+
|
|
1196
|
+
<style>
|
|
1197
|
+
* {
|
|
1198
|
+
-webkit-tap-highlight-color: transparent;
|
|
1199
|
+
}
|
|
1200
|
+
</style>
|
|
@@ -418,7 +418,7 @@ onUnmounted(() => {
|
|
|
418
418
|
gap: 4px;
|
|
419
419
|
padding: 0 5px;
|
|
420
420
|
height: 40px;
|
|
421
|
-
background: var(--klc-color-
|
|
421
|
+
background: var(--klc-color-background);
|
|
422
422
|
backdrop-filter: blur(8px);
|
|
423
423
|
-webkit-backdrop-filter: blur(8px);
|
|
424
424
|
border: 1px solid var(--klc-color-border-chart);
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<CanvasToolbar>
|
|
3
|
+
<input
|
|
4
|
+
class="range-input"
|
|
5
|
+
:value="startDate"
|
|
6
|
+
@input="$emit('update:startDate', ($event.target as HTMLInputElement).value)"
|
|
7
|
+
:placeholder="startLabel"
|
|
8
|
+
/>
|
|
9
|
+
<span class="range-sep">~</span>
|
|
10
|
+
<input
|
|
11
|
+
class="range-input"
|
|
12
|
+
:value="endDate"
|
|
13
|
+
@input="$emit('update:endDate', ($event.target as HTMLInputElement).value)"
|
|
14
|
+
:placeholder="endLabel"
|
|
15
|
+
/>
|
|
16
|
+
<span class="range-count">共 {{ count }} 条</span>
|
|
17
|
+
<button type="button" class="toolbar-btn" title="批量设置" @click="$emit('batchSetting')">
|
|
18
|
+
批量设置
|
|
19
|
+
</button>
|
|
20
|
+
<button type="button" class="toolbar-btn" title="导出" @click="$emit('export')">导出</button>
|
|
21
|
+
<button
|
|
22
|
+
type="button"
|
|
23
|
+
class="toolbar-btn toolbar-btn--delete"
|
|
24
|
+
title="取消选区"
|
|
25
|
+
@click="$emit('clear')"
|
|
26
|
+
>
|
|
27
|
+
<svg
|
|
28
|
+
class="delete-icon"
|
|
29
|
+
viewBox="0 0 24 24"
|
|
30
|
+
fill="none"
|
|
31
|
+
stroke="currentColor"
|
|
32
|
+
stroke-width="2"
|
|
33
|
+
stroke-linecap="round"
|
|
34
|
+
stroke-linejoin="round"
|
|
35
|
+
aria-hidden="true"
|
|
36
|
+
>
|
|
37
|
+
<path d="M3 6h18" />
|
|
38
|
+
<path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6" />
|
|
39
|
+
<path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2" />
|
|
40
|
+
</svg>
|
|
41
|
+
</button>
|
|
42
|
+
</CanvasToolbar>
|
|
43
|
+
</template>
|
|
44
|
+
|
|
45
|
+
<script setup lang="ts">
|
|
46
|
+
import CanvasToolbar from './common/CanvasToolbar.vue'
|
|
47
|
+
|
|
48
|
+
defineProps<{
|
|
49
|
+
startDate: string
|
|
50
|
+
endDate: string
|
|
51
|
+
startLabel: string
|
|
52
|
+
endLabel: string
|
|
53
|
+
count: number
|
|
54
|
+
}>()
|
|
55
|
+
|
|
56
|
+
defineEmits<{
|
|
57
|
+
'update:startDate': [value: string]
|
|
58
|
+
'update:endDate': [value: string]
|
|
59
|
+
export: []
|
|
60
|
+
clear: []
|
|
61
|
+
batchSetting: []
|
|
62
|
+
}>()
|
|
63
|
+
</script>
|
|
64
|
+
|
|
65
|
+
<style scoped>
|
|
66
|
+
.range-input {
|
|
67
|
+
color: var(--klc-color-axis-text);
|
|
68
|
+
font-size: 12px;
|
|
69
|
+
white-space: nowrap;
|
|
70
|
+
border: none;
|
|
71
|
+
background: transparent;
|
|
72
|
+
outline: none;
|
|
73
|
+
padding: 0 8px;
|
|
74
|
+
width: auto;
|
|
75
|
+
field-sizing: content;
|
|
76
|
+
min-width: 60px;
|
|
77
|
+
height: 26px;
|
|
78
|
+
box-sizing: border-box;
|
|
79
|
+
font-family: inherit;
|
|
80
|
+
border-radius: 4px;
|
|
81
|
+
text-align: center;
|
|
82
|
+
transition:
|
|
83
|
+
background 0.15s ease,
|
|
84
|
+
color 0.15s ease;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
.range-input::placeholder {
|
|
88
|
+
color: var(--klc-color-axis-text);
|
|
89
|
+
opacity: 0.6;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
.range-input:hover,
|
|
93
|
+
.range-input:focus {
|
|
94
|
+
background: var(--klc-color-grid-minor);
|
|
95
|
+
color: var(--klc-color-foreground);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.range-sep {
|
|
99
|
+
color: var(--klc-color-axis-text);
|
|
100
|
+
font-size: 12px;
|
|
101
|
+
opacity: 0.6;
|
|
102
|
+
user-select: none;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
.range-count {
|
|
106
|
+
color: var(--klc-color-axis-text);
|
|
107
|
+
font-size: 12px;
|
|
108
|
+
white-space: nowrap;
|
|
109
|
+
user-select: none;
|
|
110
|
+
padding: 0 8px;
|
|
111
|
+
margin-right: 4px;
|
|
112
|
+
display: flex;
|
|
113
|
+
align-items: center;
|
|
114
|
+
height: 18px;
|
|
115
|
+
border-right: 1px solid var(--klc-color-border-button);
|
|
116
|
+
}
|
|
117
|
+
</style>
|
|
@@ -268,12 +268,12 @@ watch(() => props.symbol, () => {
|
|
|
268
268
|
}
|
|
269
269
|
|
|
270
270
|
.symbol-popover {
|
|
271
|
-
z-index:
|
|
271
|
+
z-index: 110;
|
|
272
272
|
width: min(320px, calc(100vw - 24px));
|
|
273
273
|
padding: 14px;
|
|
274
274
|
border: 1px solid var(--klc-color-border-button);
|
|
275
275
|
border-radius: 3px;
|
|
276
|
-
background: var(--klc-color-
|
|
276
|
+
background: var(--klc-color-background);
|
|
277
277
|
color: var(--klc-color-foreground);
|
|
278
278
|
|
|
279
279
|
box-sizing: border-box;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div class="canvas-toolbar" @pointerdown.stop @pointermove.stop @pointerup.stop>
|
|
3
|
+
<slot />
|
|
4
|
+
</div>
|
|
5
|
+
</template>
|
|
6
|
+
|
|
7
|
+
<style scoped>
|
|
8
|
+
.canvas-toolbar {
|
|
9
|
+
display: flex;
|
|
10
|
+
align-items: center;
|
|
11
|
+
gap: 4px;
|
|
12
|
+
padding: 4px;
|
|
13
|
+
background: color-mix(in srgb, var(--klc-color-background) 92%, transparent);
|
|
14
|
+
backdrop-filter: blur(12px);
|
|
15
|
+
-webkit-backdrop-filter: blur(12px);
|
|
16
|
+
border: 1px solid var(--klc-color-border-button);
|
|
17
|
+
border-radius: 8px;
|
|
18
|
+
box-shadow:
|
|
19
|
+
0 2px 8px rgba(0, 0, 0, 0.08),
|
|
20
|
+
0 1px 2px rgba(0, 0, 0, 0.04);
|
|
21
|
+
user-select: none;
|
|
22
|
+
pointer-events: auto;
|
|
23
|
+
overflow-x: auto;
|
|
24
|
+
overflow-y: hidden;
|
|
25
|
+
scrollbar-width: none;
|
|
26
|
+
-ms-overflow-style: none;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
.canvas-toolbar::-webkit-scrollbar {
|
|
30
|
+
display: none;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
.canvas-toolbar :deep(.toolbar-btn) {
|
|
34
|
+
display: inline-flex;
|
|
35
|
+
align-items: center;
|
|
36
|
+
justify-content: center;
|
|
37
|
+
height: 26px;
|
|
38
|
+
padding: 0 10px;
|
|
39
|
+
border: none;
|
|
40
|
+
border-radius: 4px;
|
|
41
|
+
background: transparent;
|
|
42
|
+
color: var(--klc-color-axis-text);
|
|
43
|
+
font-size: 12px;
|
|
44
|
+
cursor: pointer;
|
|
45
|
+
white-space: nowrap;
|
|
46
|
+
transition:
|
|
47
|
+
background 0.15s ease,
|
|
48
|
+
color 0.15s ease;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.canvas-toolbar :deep(.toolbar-btn:hover) {
|
|
52
|
+
background: var(--klc-color-grid-minor);
|
|
53
|
+
color: var(--klc-color-foreground);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
.canvas-toolbar :deep(.toolbar-btn--delete) {
|
|
57
|
+
width: 26px;
|
|
58
|
+
padding: 0;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.canvas-toolbar :deep(.toolbar-btn--delete:hover) {
|
|
62
|
+
color: #dc2626;
|
|
63
|
+
background: color-mix(in srgb, #dc2626 10%, transparent);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
.canvas-toolbar :deep(.delete-icon) {
|
|
67
|
+
width: 14px;
|
|
68
|
+
height: 14px;
|
|
69
|
+
}
|
|
70
|
+
</style>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
class="canvas-toolbar-stack"
|
|
4
|
+
@pointerdown.stop
|
|
5
|
+
@pointermove.stop
|
|
6
|
+
@pointerup.stop
|
|
7
|
+
>
|
|
8
|
+
<slot />
|
|
9
|
+
</div>
|
|
10
|
+
</template>
|
|
11
|
+
|
|
12
|
+
<style scoped>
|
|
13
|
+
.canvas-toolbar-stack {
|
|
14
|
+
position: absolute;
|
|
15
|
+
left: 50%;
|
|
16
|
+
top: 10px;
|
|
17
|
+
transform: translateX(-50%);
|
|
18
|
+
display: flex;
|
|
19
|
+
flex-direction: column;
|
|
20
|
+
align-items: stretch;
|
|
21
|
+
gap: 8px;
|
|
22
|
+
z-index: 100;
|
|
23
|
+
pointer-events: none;
|
|
24
|
+
max-width: calc(100% - 20px);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@media (max-width: 768px) {
|
|
28
|
+
.canvas-toolbar-stack {
|
|
29
|
+
max-width: 90%;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
</style>
|
|
@@ -108,6 +108,12 @@ export function useRangeSelection(options: {
|
|
|
108
108
|
return fmtDate(data[bounds.end])
|
|
109
109
|
})
|
|
110
110
|
|
|
111
|
+
const rangeSelectionCount = computed(() => {
|
|
112
|
+
const bounds = rangeSelectionBounds.value
|
|
113
|
+
if (!bounds) return 0
|
|
114
|
+
return bounds.end - bounds.start + 1
|
|
115
|
+
})
|
|
116
|
+
|
|
111
117
|
const rangeSelectionOverlayStyle = computed(() => {
|
|
112
118
|
const bounds = rangeSelectionBounds.value
|
|
113
119
|
if (!bounds) return null
|
|
@@ -399,6 +405,7 @@ export function useRangeSelection(options: {
|
|
|
399
405
|
isRangeSelectActive,
|
|
400
406
|
rangeSelectionReady,
|
|
401
407
|
rangeSelectionBounds,
|
|
408
|
+
rangeSelectionCount,
|
|
402
409
|
rangeSelectionStartLabel,
|
|
403
410
|
rangeSelectionEndLabel,
|
|
404
411
|
rangeSelectionOverlayStyle,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ref, type Ref } from 'vue'
|
|
1
|
+
import { ref, nextTick, type Ref } from 'vue'
|
|
2
2
|
|
|
3
3
|
export function useTeleportedPopup(
|
|
4
4
|
triggerRef: Ref<HTMLElement | null>,
|
|
@@ -11,15 +11,28 @@ export function useTeleportedPopup(
|
|
|
11
11
|
const trigger = triggerRef.value
|
|
12
12
|
if (!trigger) return
|
|
13
13
|
const rect = trigger.getBoundingClientRect()
|
|
14
|
+
const popup = popupRef.value
|
|
15
|
+
|
|
16
|
+
let left = rect.left
|
|
17
|
+
if (popup) {
|
|
18
|
+
const popupWidth = popup.offsetWidth
|
|
19
|
+
const viewportWidth = window.innerWidth
|
|
20
|
+
const margin = 8
|
|
21
|
+
if (left + popupWidth > viewportWidth - margin) {
|
|
22
|
+
left = Math.max(margin, viewportWidth - popupWidth - margin)
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
14
26
|
popupStyle.value = {
|
|
15
27
|
position: 'fixed',
|
|
16
28
|
top: `${rect.bottom + gap}px`,
|
|
17
|
-
left: `${
|
|
29
|
+
left: `${left}px`,
|
|
18
30
|
}
|
|
19
31
|
}
|
|
20
32
|
|
|
21
33
|
function startPositionSync() {
|
|
22
34
|
updatePosition()
|
|
35
|
+
nextTick(() => updatePosition())
|
|
23
36
|
document.addEventListener('scroll', updatePosition, { capture: true, passive: true })
|
|
24
37
|
window.addEventListener('resize', updatePosition, { passive: true })
|
|
25
38
|
}
|