@katlux/block-charts 0.1.1-alpha.87 → 0.1.1-alpha.89
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/module.d.mts +3 -1
- package/dist/module.json +1 -1
- package/dist/runtime/components/KAreaChart/KAreaChart.vue +233 -60
- package/dist/runtime/components/KBarChart/KBarChart.vue +237 -114
- package/dist/runtime/components/KHeatMap/KHeatMap.vue +28 -15
- package/dist/runtime/components/KLineChart/KLineChart.vue +149 -41
- package/dist/runtime/components/KPieChart/KPieChart.vue +25 -26
- package/dist/runtime/components/KScatterChart/KScatterChart.vue +84 -32
- package/dist/runtime/composables/chartUtils.d.ts +8 -0
- package/dist/runtime/composables/chartUtils.js +58 -0
- package/dist/runtime/composables/useChartAxes.d.ts +3 -1
- package/dist/runtime/composables/useChartAxes.js +26 -9
- package/dist/runtime/composables/useChartData.d.ts +4 -3
- package/dist/runtime/composables/useChartData.js +19 -18
- package/dist/runtime/composables/useChartExport.d.ts +1 -1
- package/dist/runtime/composables/useChartExport.js +18 -2
- package/dist/runtime/composables/useChartHitTest.d.ts +1 -1
- package/dist/runtime/composables/useChartHitTest.js +4 -3
- package/package.json +1 -1
package/dist/module.d.mts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import * as _nuxt_schema from '@nuxt/schema';
|
|
2
|
+
|
|
1
3
|
interface ModuleOptions {
|
|
2
4
|
}
|
|
3
|
-
declare const _default:
|
|
5
|
+
declare const _default: _nuxt_schema.NuxtModule<ModuleOptions, ModuleOptions, false>;
|
|
4
6
|
|
|
5
7
|
export { _default as default };
|
|
6
8
|
export type { ModuleOptions };
|
package/dist/module.json
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
<template lang="pug">
|
|
2
2
|
.k-chart-wrapper(
|
|
3
3
|
:class="{ 'k-chart--grabbing': isDragging, 'k-chart--pannable': canPan }"
|
|
4
|
-
:style="{ cursor: isDragging ? 'grabbing' : canPan ? 'grab' : 'default' }"
|
|
5
4
|
@wheel.prevent="onWheel"
|
|
6
5
|
@mousedown="onMouseDown"
|
|
7
6
|
@mousemove="onMouseMove"
|
|
@@ -30,7 +29,6 @@
|
|
|
30
29
|
:height="axes.plotHeight.value"
|
|
31
30
|
)
|
|
32
31
|
g.k-chart-hits
|
|
33
|
-
// No more rect v-for here.
|
|
34
32
|
|
|
35
33
|
// Dynamic Interaction Layer
|
|
36
34
|
g.k-chart-interaction(v-if="hoveredIndex !== -1" clip-path="url(#plot-clip)")
|
|
@@ -41,7 +39,6 @@
|
|
|
41
39
|
:cy="pt.y"
|
|
42
40
|
:r="10"
|
|
43
41
|
fill="transparent"
|
|
44
|
-
style="cursor: pointer"
|
|
45
42
|
@click="emit('click-point', pt.item, pt.index)"
|
|
46
43
|
)
|
|
47
44
|
circle(
|
|
@@ -57,7 +54,7 @@
|
|
|
57
54
|
)
|
|
58
55
|
.k-chart-tooltip(
|
|
59
56
|
v-if="tooltipState.visible && !isLoading"
|
|
60
|
-
:style="
|
|
57
|
+
:style="tooltipStyle"
|
|
61
58
|
)
|
|
62
59
|
slot(name="tooltip" :item="tooltipState.item" :index="tooltipState.index")
|
|
63
60
|
span {{ tooltipState.content }}
|
|
@@ -73,6 +70,7 @@ import { useChartViewport } from "../../composables/useChartViewport";
|
|
|
73
70
|
import { useChartAnimation, easeOut } from "../../composables/useChartAnimation";
|
|
74
71
|
import { useChartExport } from "../../composables/useChartExport";
|
|
75
72
|
import { useChartHitTest } from "../../composables/useChartHitTest";
|
|
73
|
+
import { cleanNumber, safeMax } from "../../composables/chartUtils";
|
|
76
74
|
const props = defineProps({
|
|
77
75
|
dataProvider: { type: null, required: false },
|
|
78
76
|
xField: { type: String, required: false, default: "x" },
|
|
@@ -92,7 +90,7 @@ const emit = defineEmits(["click-point", "hover-point", "zoom-change", "pan-chan
|
|
|
92
90
|
const containerRef = ref(null);
|
|
93
91
|
const canvasRef = ref(null);
|
|
94
92
|
const svgRef = ref(null);
|
|
95
|
-
const { ctx, width, height, clear, setupCanvas } = useChartCanvas();
|
|
93
|
+
const { ctx, width, height, resizeTick, clear, setupCanvas } = useChartCanvas();
|
|
96
94
|
const { hoveredIndex, tooltipState, showTooltip, hideTooltip, setHovered } = useChartSvg();
|
|
97
95
|
const dataRef = computed(() => props.dataProvider);
|
|
98
96
|
const { items } = useChartData(dataRef);
|
|
@@ -101,6 +99,10 @@ const { exportPng, exportSvg: exportSvgFile } = useChartExport();
|
|
|
101
99
|
const isLoading = computed(() => {
|
|
102
100
|
return props.dataProvider?.loading?.value || props.dataProvider?.initialLoad?.value || false;
|
|
103
101
|
});
|
|
102
|
+
const tooltipStyle = computed(() => ({
|
|
103
|
+
left: `${tooltipState.value.x}px`,
|
|
104
|
+
top: `${tooltipState.value.y}px`
|
|
105
|
+
}));
|
|
104
106
|
const maxZoomRef = computed(() => props.maxZoom);
|
|
105
107
|
const plotWidthRef = computed(() => Math.max(0, width.value - 75));
|
|
106
108
|
const plotLeftRef = computed(() => 55);
|
|
@@ -122,6 +124,45 @@ const {
|
|
|
122
124
|
} = viewport;
|
|
123
125
|
const xFieldRef = computed(() => props.xField);
|
|
124
126
|
const yFieldRef = computed(() => props.yField);
|
|
127
|
+
const xAxisType = computed(() => {
|
|
128
|
+
if (!items.value.length) return "linear";
|
|
129
|
+
const firstVal = items.value[0]?.[props.xField];
|
|
130
|
+
if (typeof firstVal === "number") return "linear";
|
|
131
|
+
if (typeof firstVal === "string") {
|
|
132
|
+
const parsed = Date.parse(firstVal);
|
|
133
|
+
if (!isNaN(parsed) && !/^\d+$/.test(firstVal.trim())) return "datetime";
|
|
134
|
+
const num = Number(firstVal);
|
|
135
|
+
if (!isNaN(num)) return "linear";
|
|
136
|
+
return "category";
|
|
137
|
+
}
|
|
138
|
+
return "linear";
|
|
139
|
+
});
|
|
140
|
+
const categoryList = computed(() => {
|
|
141
|
+
if (xAxisType.value !== "category") return [];
|
|
142
|
+
const seen = /* @__PURE__ */ new Set();
|
|
143
|
+
const list = [];
|
|
144
|
+
for (const item of items.value) {
|
|
145
|
+
const val = String(item[props.xField] ?? "");
|
|
146
|
+
if (!seen.has(val)) {
|
|
147
|
+
seen.add(val);
|
|
148
|
+
list.push(val);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
return list;
|
|
152
|
+
});
|
|
153
|
+
const categoryIndexMap = computed(() => {
|
|
154
|
+
const map = {};
|
|
155
|
+
categoryList.value.forEach((cat, i) => {
|
|
156
|
+
map[cat] = i;
|
|
157
|
+
});
|
|
158
|
+
return map;
|
|
159
|
+
});
|
|
160
|
+
const resolveX = (item, index = 0) => {
|
|
161
|
+
if (!item) return index;
|
|
162
|
+
const raw = item[props.xField];
|
|
163
|
+
if (xAxisType.value === "category") return categoryIndexMap.value[String(raw)] ?? index;
|
|
164
|
+
return parseValue(raw, index);
|
|
165
|
+
};
|
|
125
166
|
const seriesMap = computed(() => {
|
|
126
167
|
const map = /* @__PURE__ */ new Map();
|
|
127
168
|
for (const item of items.value) {
|
|
@@ -131,12 +172,60 @@ const seriesMap = computed(() => {
|
|
|
131
172
|
}
|
|
132
173
|
return map;
|
|
133
174
|
});
|
|
134
|
-
const
|
|
175
|
+
const sortedSeriesMap = computed(() => {
|
|
176
|
+
const map = /* @__PURE__ */ new Map();
|
|
177
|
+
const itemIndexMap = /* @__PURE__ */ new Map();
|
|
178
|
+
for (let i = 0; i < items.value.length; i++) {
|
|
179
|
+
itemIndexMap.set(items.value[i], i);
|
|
180
|
+
}
|
|
181
|
+
for (const [key, seriesItems] of seriesMap.value) {
|
|
182
|
+
const enriched = seriesItems.map((item, idx) => ({
|
|
183
|
+
item,
|
|
184
|
+
index: itemIndexMap.get(item) ?? idx,
|
|
185
|
+
xVal: resolveX(item, idx),
|
|
186
|
+
yVal: cleanNumber(item[props.yField])
|
|
187
|
+
})).sort((a, b) => a.xVal - b.xVal);
|
|
188
|
+
map.set(key, enriched);
|
|
189
|
+
}
|
|
190
|
+
return map;
|
|
191
|
+
});
|
|
192
|
+
const stackedMaxY = computed(() => {
|
|
193
|
+
if (!props.stacked) return 0;
|
|
194
|
+
const xTotals = /* @__PURE__ */ new Map();
|
|
195
|
+
for (const [, seriesItems] of sortedSeriesMap.value) {
|
|
196
|
+
for (const pt of seriesItems) {
|
|
197
|
+
const current = xTotals.get(pt.xVal) || 0;
|
|
198
|
+
xTotals.set(pt.xVal, current + Math.max(0, pt.yVal));
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return safeMax(Array.from(xTotals.values()), 10);
|
|
202
|
+
});
|
|
203
|
+
const axes = useChartAxes({
|
|
204
|
+
items,
|
|
205
|
+
xField: xFieldRef,
|
|
206
|
+
yField: yFieldRef,
|
|
207
|
+
width,
|
|
208
|
+
height,
|
|
209
|
+
scale,
|
|
210
|
+
panOffset,
|
|
211
|
+
padding: { top: 20, right: 20, bottom: 40, left: 55 },
|
|
212
|
+
xValueGetter: (r, i) => resolveX(r, i)
|
|
213
|
+
});
|
|
214
|
+
const effectiveYMax = computed(() => {
|
|
215
|
+
return props.stacked ? Math.max(stackedMaxY.value, axes.yMax.value) : axes.yMax.value;
|
|
216
|
+
});
|
|
217
|
+
const toStackedY = (val) => {
|
|
218
|
+
const yMin = axes.yMin.value;
|
|
219
|
+
const yMax = effectiveYMax.value;
|
|
220
|
+
const range = yMax - yMin;
|
|
221
|
+
if (range === 0) return axes.plotTop + axes.plotHeight.value;
|
|
222
|
+
return axes.plotTop + axes.plotHeight.value - (val - yMin) / range * axes.plotHeight.value;
|
|
223
|
+
};
|
|
135
224
|
const draw = () => {
|
|
136
225
|
if (!ctx.value) return;
|
|
137
226
|
clear(props.backgroundColor);
|
|
138
227
|
const c = ctx.value;
|
|
139
|
-
const p = easeOut(progress.value);
|
|
228
|
+
const p = props.animated ? easeOut(progress.value) : 1;
|
|
140
229
|
c.save();
|
|
141
230
|
c.strokeStyle = "rgba(100,100,120,0.1)";
|
|
142
231
|
c.lineWidth = 1;
|
|
@@ -160,17 +249,45 @@ const draw = () => {
|
|
|
160
249
|
c.font = "11px system-ui, sans-serif";
|
|
161
250
|
c.textAlign = "right";
|
|
162
251
|
for (let i = 0; i <= 5; i++) {
|
|
163
|
-
const val = axes.yMin.value + (
|
|
164
|
-
c.fillText(val.toFixed(1), axes.plotLeft - 8,
|
|
252
|
+
const val = axes.yMin.value + (effectiveYMax.value - axes.yMin.value) * (i / 5);
|
|
253
|
+
c.fillText(val.toFixed(1), axes.plotLeft - 8, toStackedY(val) + 4);
|
|
165
254
|
}
|
|
166
255
|
c.textAlign = "center";
|
|
167
|
-
|
|
168
|
-
const
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
256
|
+
if (xAxisType.value === "category") {
|
|
257
|
+
const count = categoryList.value.length;
|
|
258
|
+
if (count > 0) {
|
|
259
|
+
const xSteps = Math.min(10, count - 1);
|
|
260
|
+
const stepSize = xSteps > 0 ? (count - 1) / xSteps : 1;
|
|
261
|
+
for (let i = 0; i <= xSteps; i++) {
|
|
262
|
+
const idx = Math.round(i * stepSize);
|
|
263
|
+
const x = axes.toX(idx);
|
|
264
|
+
const label = categoryList.value[idx] ?? "";
|
|
265
|
+
c.fillText(label, x, axes.plotTop + axes.plotHeight.value + 16);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
} else {
|
|
269
|
+
for (let i = 0; i <= 5; i++) {
|
|
270
|
+
const val = axes.xMin.value + (axes.xMax.value - axes.xMin.value) * (i / 5);
|
|
271
|
+
const x = axes.toX(val);
|
|
272
|
+
let label;
|
|
273
|
+
if (xAxisType.value === "datetime") {
|
|
274
|
+
const allMidnight = items.value.every((r) => {
|
|
275
|
+
const ts = parseValue(r[props.xField]);
|
|
276
|
+
const d = new Date(ts);
|
|
277
|
+
return d.getHours() === 0 && d.getMinutes() === 0;
|
|
278
|
+
});
|
|
279
|
+
label = allMidnight ? new Date(val).toLocaleDateString([], { month: "short", day: "numeric" }) : new Date(val).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
|
|
280
|
+
} else {
|
|
281
|
+
label = val.toFixed(1);
|
|
282
|
+
}
|
|
283
|
+
c.fillText(label, x, axes.plotTop + axes.plotHeight.value + 16);
|
|
284
|
+
}
|
|
173
285
|
}
|
|
286
|
+
c.strokeStyle = "rgba(150,150,170,0.5)";
|
|
287
|
+
c.beginPath();
|
|
288
|
+
c.moveTo(axes.plotLeft, axes.plotTop + axes.plotHeight.value);
|
|
289
|
+
c.lineTo(axes.plotLeft + axes.plotWidth.value, axes.plotTop + axes.plotHeight.value);
|
|
290
|
+
c.stroke();
|
|
174
291
|
c.beginPath();
|
|
175
292
|
c.moveTo(axes.plotLeft, axes.plotTop);
|
|
176
293
|
c.lineTo(axes.plotLeft, axes.plotTop + axes.plotHeight.value);
|
|
@@ -194,41 +311,74 @@ const draw = () => {
|
|
|
194
311
|
c.beginPath();
|
|
195
312
|
c.rect(axes.plotLeft, axes.plotTop, axes.plotWidth.value, axes.plotHeight.value);
|
|
196
313
|
c.clip();
|
|
314
|
+
const currentBaseline = /* @__PURE__ */ new Map();
|
|
197
315
|
let seriesIdx = 0;
|
|
198
|
-
for (const [
|
|
316
|
+
for (const [, seriesItems] of sortedSeriesMap.value) {
|
|
199
317
|
const color = props.colors[seriesIdx % props.colors.length];
|
|
200
|
-
const
|
|
201
|
-
const
|
|
202
|
-
const pts = sorted.slice(0, drawCount);
|
|
318
|
+
const drawCount = Math.max(1, Math.floor(seriesItems.length * p));
|
|
319
|
+
const pts = seriesItems.slice(0, drawCount);
|
|
203
320
|
c.save();
|
|
204
321
|
c.beginPath();
|
|
205
|
-
|
|
206
|
-
const
|
|
207
|
-
const
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
322
|
+
if (props.stacked) {
|
|
323
|
+
const topPoints = [];
|
|
324
|
+
const bottomPoints = [];
|
|
325
|
+
for (const pt of pts) {
|
|
326
|
+
const x = axes.toX(pt.xVal);
|
|
327
|
+
const base = currentBaseline.get(pt.xVal) || 0;
|
|
328
|
+
const top = base + pt.yVal;
|
|
329
|
+
const yTop = toStackedY(top);
|
|
330
|
+
const yBottom = toStackedY(base);
|
|
331
|
+
topPoints.push({ x, y: yTop });
|
|
332
|
+
bottomPoints.push({ x, y: yBottom });
|
|
333
|
+
currentBaseline.set(pt.xVal, top);
|
|
334
|
+
}
|
|
335
|
+
topPoints.forEach((pt, i) => {
|
|
336
|
+
if (i === 0) c.moveTo(pt.x, pt.y);
|
|
337
|
+
else c.lineTo(pt.x, pt.y);
|
|
338
|
+
});
|
|
339
|
+
for (let i = bottomPoints.length - 1; i >= 0; i--) {
|
|
340
|
+
c.lineTo(bottomPoints[i].x, bottomPoints[i].y);
|
|
341
|
+
}
|
|
217
342
|
c.closePath();
|
|
343
|
+
c.fillStyle = color + "44";
|
|
344
|
+
c.fill();
|
|
345
|
+
c.beginPath();
|
|
346
|
+
topPoints.forEach((pt, i) => {
|
|
347
|
+
if (i === 0) c.moveTo(pt.x, pt.y);
|
|
348
|
+
else c.lineTo(pt.x, pt.y);
|
|
349
|
+
});
|
|
350
|
+
c.strokeStyle = color;
|
|
351
|
+
c.lineWidth = 2;
|
|
352
|
+
c.stroke();
|
|
353
|
+
} else {
|
|
354
|
+
pts.forEach((pt, i) => {
|
|
355
|
+
const x = axes.toX(pt.xVal);
|
|
356
|
+
const y = toStackedY(pt.yVal);
|
|
357
|
+
if (i === 0) c.moveTo(x, y);
|
|
358
|
+
else c.lineTo(x, y);
|
|
359
|
+
});
|
|
360
|
+
if (pts.length > 0) {
|
|
361
|
+
const lastX = axes.toX(pts[pts.length - 1].xVal);
|
|
362
|
+
const firstX = axes.toX(pts[0].xVal);
|
|
363
|
+
const baselineY = axes.plotTop + axes.plotHeight.value;
|
|
364
|
+
c.lineTo(lastX, baselineY);
|
|
365
|
+
c.lineTo(firstX, baselineY);
|
|
366
|
+
c.closePath();
|
|
367
|
+
}
|
|
368
|
+
c.fillStyle = color + "33";
|
|
369
|
+
c.fill();
|
|
370
|
+
c.beginPath();
|
|
371
|
+
pts.forEach((pt, i) => {
|
|
372
|
+
const x = axes.toX(pt.xVal);
|
|
373
|
+
const y = toStackedY(pt.yVal);
|
|
374
|
+
if (i === 0) c.moveTo(x, y);
|
|
375
|
+
else c.lineTo(x, y);
|
|
376
|
+
});
|
|
377
|
+
c.strokeStyle = color;
|
|
378
|
+
c.lineWidth = 2;
|
|
379
|
+
c.lineJoin = "round";
|
|
380
|
+
c.stroke();
|
|
218
381
|
}
|
|
219
|
-
c.fillStyle = color + "33";
|
|
220
|
-
c.fill();
|
|
221
|
-
c.beginPath();
|
|
222
|
-
pts.forEach((item, i) => {
|
|
223
|
-
const x = axes.toX(parseValue(item[props.xField]));
|
|
224
|
-
const y = axes.toY(parseValue(item[props.yField]));
|
|
225
|
-
if (i === 0) c.moveTo(x, y);
|
|
226
|
-
else c.lineTo(x, y);
|
|
227
|
-
});
|
|
228
|
-
c.strokeStyle = color;
|
|
229
|
-
c.lineWidth = 2;
|
|
230
|
-
c.lineJoin = "round";
|
|
231
|
-
c.stroke();
|
|
232
382
|
c.restore();
|
|
233
383
|
seriesIdx++;
|
|
234
384
|
}
|
|
@@ -256,28 +406,34 @@ const drawLegend = (c) => {
|
|
|
256
406
|
});
|
|
257
407
|
c.restore();
|
|
258
408
|
};
|
|
259
|
-
watch(
|
|
409
|
+
watch(progress, draw);
|
|
410
|
+
watch([scale, panOffset, hoveredIndex, resizeTick, width, height, () => props.stacked], draw);
|
|
260
411
|
watch(items, () => {
|
|
261
412
|
if (props.animated) animate();
|
|
262
|
-
|
|
413
|
+
else draw();
|
|
414
|
+
});
|
|
263
415
|
watch(scale, (v) => emit("zoom-change", v));
|
|
264
416
|
watch(panOffset, (v) => emit("pan-change", v), { deep: true });
|
|
265
417
|
const hoveredPoints = computed(() => {
|
|
266
418
|
if (hoveredIndex.value === -1) return [];
|
|
419
|
+
if (hoveredIndex.value >= items.value.length) {
|
|
420
|
+
setHovered(-1);
|
|
421
|
+
return [];
|
|
422
|
+
}
|
|
267
423
|
const item = items.value[hoveredIndex.value];
|
|
268
|
-
const xVal =
|
|
424
|
+
const xVal = resolveX(item);
|
|
269
425
|
const xPx = axes.toX(xVal);
|
|
270
426
|
const pts = [];
|
|
271
427
|
let seriesIdx = 0;
|
|
272
|
-
for (const [
|
|
273
|
-
const
|
|
274
|
-
if (
|
|
428
|
+
for (const [, seriesList] of sortedSeriesMap.value) {
|
|
429
|
+
const found = seriesList.find((pt) => pt.xVal === xVal);
|
|
430
|
+
if (found) {
|
|
275
431
|
pts.push({
|
|
276
432
|
x: xPx,
|
|
277
|
-
y:
|
|
433
|
+
y: toStackedY(found.yVal),
|
|
278
434
|
color: props.colors[seriesIdx % props.colors.length],
|
|
279
|
-
item:
|
|
280
|
-
index:
|
|
435
|
+
item: found.item,
|
|
436
|
+
index: found.index
|
|
281
437
|
});
|
|
282
438
|
}
|
|
283
439
|
seriesIdx++;
|
|
@@ -293,7 +449,7 @@ onMounted(async () => {
|
|
|
293
449
|
}
|
|
294
450
|
});
|
|
295
451
|
const onMouseMove = (e) => {
|
|
296
|
-
|
|
452
|
+
onViewportMouseMove(e);
|
|
297
453
|
if (isDragging.value) {
|
|
298
454
|
setHovered(-1);
|
|
299
455
|
hideTooltip();
|
|
@@ -303,11 +459,24 @@ const onMouseMove = (e) => {
|
|
|
303
459
|
if (!rect) return;
|
|
304
460
|
const mouseX = e.clientX - rect.left;
|
|
305
461
|
const mouseY = e.clientY - rect.top;
|
|
306
|
-
|
|
462
|
+
if (mouseX < axes.plotLeft || mouseX > axes.plotLeft + axes.plotWidth.value || mouseY < axes.plotTop || mouseY > axes.plotTop + axes.plotHeight.value) {
|
|
463
|
+
setHovered(-1);
|
|
464
|
+
hideTooltip();
|
|
465
|
+
return;
|
|
466
|
+
}
|
|
467
|
+
const nearest = findNearestX(mouseX, items.value, axes.toX, (r, i) => resolveX(r, i));
|
|
307
468
|
if (nearest) {
|
|
308
469
|
setHovered(nearest.index);
|
|
309
|
-
showTooltip(mouseX + 10, mouseY - 20, `${nearest.item[props.xField]}: ${nearest.item[props.yField]}`, nearest.item, nearest.index);
|
|
310
470
|
emit("hover-point", nearest.item, nearest.index);
|
|
471
|
+
const xPx = axes.toX(resolveX(nearest.item, nearest.index));
|
|
472
|
+
const yPx = toStackedY(cleanNumber(nearest.item[props.yField]));
|
|
473
|
+
showTooltip(
|
|
474
|
+
xPx,
|
|
475
|
+
yPx - 10,
|
|
476
|
+
`${props.xField}: ${nearest.item[props.xField]} | ${props.yField}: ${nearest.item[props.yField]}`,
|
|
477
|
+
nearest.item,
|
|
478
|
+
nearest.index
|
|
479
|
+
);
|
|
311
480
|
} else {
|
|
312
481
|
setHovered(-1);
|
|
313
482
|
hideTooltip();
|
|
@@ -323,7 +492,7 @@ defineExpose({
|
|
|
323
492
|
zoomOut: viewport.zoomOut,
|
|
324
493
|
resetZoom: viewport.resetZoom,
|
|
325
494
|
exportPng: () => canvasRef.value && exportPng(canvasRef.value, "area-chart.png"),
|
|
326
|
-
exportSvg: () => svgRef.value && exportSvgFile(svgRef.value, "area-chart.svg")
|
|
495
|
+
exportSvg: () => svgRef.value && exportSvgFile(svgRef.value, "area-chart.svg", canvasRef.value)
|
|
327
496
|
});
|
|
328
497
|
</script>
|
|
329
498
|
|
|
@@ -331,6 +500,13 @@ defineExpose({
|
|
|
331
500
|
.k-chart-wrapper {
|
|
332
501
|
position: relative;
|
|
333
502
|
user-select: none;
|
|
503
|
+
cursor: default;
|
|
504
|
+
}
|
|
505
|
+
.k-chart-wrapper.k-chart--pannable {
|
|
506
|
+
cursor: grab;
|
|
507
|
+
}
|
|
508
|
+
.k-chart-wrapper.k-chart--grabbing {
|
|
509
|
+
cursor: grabbing;
|
|
334
510
|
}
|
|
335
511
|
.k-chart-wrapper .k-chart-controls {
|
|
336
512
|
position: absolute;
|
|
@@ -340,9 +516,6 @@ defineExpose({
|
|
|
340
516
|
display: flex;
|
|
341
517
|
gap: 4px;
|
|
342
518
|
}
|
|
343
|
-
.k-chart-wrapper .k-chart-btn {
|
|
344
|
-
display: none;
|
|
345
|
-
}
|
|
346
519
|
.k-chart-wrapper .k-chart-inner {
|
|
347
520
|
width: 100%;
|
|
348
521
|
height: 400px;
|