@katlux/block-charts 0.1.1-alpha.87 → 0.1.1-alpha.88

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.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "compatibility": {
5
5
  "nuxt": "^3.0.0"
6
6
  },
7
- "version": "0.1.1-alpha.87",
7
+ "version": "0.1.1-alpha.88",
8
8
  "builder": {
9
9
  "@nuxt/module-builder": "1.0.2",
10
10
  "unbuild": "3.6.1"
@@ -30,7 +30,6 @@
30
30
  :height="axes.plotHeight.value"
31
31
  )
32
32
  g.k-chart-hits
33
- // No more rect v-for here.
34
33
 
35
34
  // Dynamic Interaction Layer
36
35
  g.k-chart-interaction(v-if="hoveredIndex !== -1" clip-path="url(#plot-clip)")
@@ -122,6 +121,34 @@ const {
122
121
  } = viewport;
123
122
  const xFieldRef = computed(() => props.xField);
124
123
  const yFieldRef = computed(() => props.yField);
124
+ const xAxisType = computed(() => {
125
+ if (!items.value.length) return "numeric";
126
+ const sample = items.value[0][props.xField];
127
+ if (typeof sample === "number") return "numeric";
128
+ if (typeof sample === "string") {
129
+ const ts = Date.parse(sample);
130
+ if (!isNaN(ts) && isNaN(Number(sample))) return "datetime";
131
+ return "category";
132
+ }
133
+ return "numeric";
134
+ });
135
+ const categoryList = computed(() => {
136
+ if (xAxisType.value !== "category") return [];
137
+ return [...new Set(items.value.map((r) => String(r[props.xField])))];
138
+ });
139
+ const categoryIndexMap = computed(() => {
140
+ const map = {};
141
+ categoryList.value.forEach((cat, i) => {
142
+ map[cat] = i;
143
+ });
144
+ return map;
145
+ });
146
+ const resolveX = (item, index = 0) => {
147
+ if (!item) return index;
148
+ const raw = item[props.xField];
149
+ if (xAxisType.value === "category") return categoryIndexMap.value[String(raw)] ?? index;
150
+ return parseValue(raw, index);
151
+ };
125
152
  const seriesMap = computed(() => {
126
153
  const map = /* @__PURE__ */ new Map();
127
154
  for (const item of items.value) {
@@ -131,7 +158,17 @@ const seriesMap = computed(() => {
131
158
  }
132
159
  return map;
133
160
  });
134
- const axes = useChartAxes({ items, xField: xFieldRef, yField: yFieldRef, width, height, scale, panOffset, padding: { top: 20, right: 20, bottom: 40, left: 55 } });
161
+ const axes = useChartAxes({
162
+ items,
163
+ xField: xFieldRef,
164
+ yField: yFieldRef,
165
+ width,
166
+ height,
167
+ scale,
168
+ panOffset,
169
+ padding: { top: 20, right: 20, bottom: 40, left: 55 },
170
+ xValueGetter: (r, i) => resolveX(r, i)
171
+ });
135
172
  const draw = () => {
136
173
  if (!ctx.value) return;
137
174
  clear(props.backgroundColor);
@@ -164,12 +201,35 @@ const draw = () => {
164
201
  c.fillText(val.toFixed(1), axes.plotLeft - 8, axes.toY(val) + 4);
165
202
  }
166
203
  c.textAlign = "center";
167
- for (let i = 0; i <= 5; i++) {
168
- const val = axes.xMin.value + (axes.xMax.value - axes.xMin.value) * (i / 5);
169
- const x = axes.toX(val);
170
- let label = val.toFixed(1);
171
- if (val > 1e12) label = new Date(val).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
172
- c.fillText(label, x, axes.plotTop + axes.plotHeight.value + 16);
204
+ if (xAxisType.value === "category") {
205
+ const count = categoryList.value.length;
206
+ if (count > 0) {
207
+ const xSteps = Math.min(10, count - 1);
208
+ const stepSize = xSteps > 0 ? (count - 1) / xSteps : 1;
209
+ for (let i = 0; i <= xSteps; i++) {
210
+ const idx = Math.round(i * stepSize);
211
+ const x = axes.toX(idx);
212
+ const label = categoryList.value[idx] ?? "";
213
+ c.fillText(label, x, axes.plotTop + axes.plotHeight.value + 16);
214
+ }
215
+ }
216
+ } else {
217
+ for (let i = 0; i <= 5; i++) {
218
+ const val = axes.xMin.value + (axes.xMax.value - axes.xMin.value) * (i / 5);
219
+ const x = axes.toX(val);
220
+ let label;
221
+ if (xAxisType.value === "datetime") {
222
+ const allMidnight = items.value.every((r) => {
223
+ const ts = parseValue(r[props.xField]);
224
+ const d = new Date(ts);
225
+ return d.getHours() === 0 && d.getMinutes() === 0;
226
+ });
227
+ label = allMidnight ? new Date(val).toLocaleDateString([], { month: "short", day: "numeric" }) : new Date(val).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
228
+ } else {
229
+ label = val.toFixed(1);
230
+ }
231
+ c.fillText(label, x, axes.plotTop + axes.plotHeight.value + 16);
232
+ }
173
233
  }
174
234
  c.beginPath();
175
235
  c.moveTo(axes.plotLeft, axes.plotTop);
@@ -195,22 +255,22 @@ const draw = () => {
195
255
  c.rect(axes.plotLeft, axes.plotTop, axes.plotWidth.value, axes.plotHeight.value);
196
256
  c.clip();
197
257
  let seriesIdx = 0;
198
- for (const [key, seriesItems] of seriesMap.value) {
258
+ for (const [, seriesItems] of seriesMap.value) {
199
259
  const color = props.colors[seriesIdx % props.colors.length];
200
- const sorted = [...seriesItems].sort((a, b) => parseValue(a[props.xField]) - parseValue(b[props.xField]));
260
+ const sorted = [...seriesItems].sort((a, b) => resolveX(a) - resolveX(b));
201
261
  const drawCount = Math.max(1, Math.floor(sorted.length * p));
202
262
  const pts = sorted.slice(0, drawCount);
203
263
  c.save();
204
264
  c.beginPath();
205
265
  pts.forEach((item, i) => {
206
- const x = axes.toX(parseValue(item[props.xField]));
266
+ const x = axes.toX(resolveX(item, i));
207
267
  const y = axes.toY(parseValue(item[props.yField]));
208
268
  if (i === 0) c.moveTo(x, y);
209
269
  else c.lineTo(x, y);
210
270
  });
211
271
  if (pts.length > 0) {
212
- const lastX = axes.toX(parseValue(pts[pts.length - 1][props.xField]));
213
- const firstX = axes.toX(parseValue(pts[0][props.xField]));
272
+ const lastX = axes.toX(resolveX(pts[pts.length - 1], pts.length - 1));
273
+ const firstX = axes.toX(resolveX(pts[0], 0));
214
274
  const baselineY = axes.plotTop + axes.plotHeight.value;
215
275
  c.lineTo(lastX, baselineY);
216
276
  c.lineTo(firstX, baselineY);
@@ -220,7 +280,7 @@ const draw = () => {
220
280
  c.fill();
221
281
  c.beginPath();
222
282
  pts.forEach((item, i) => {
223
- const x = axes.toX(parseValue(item[props.xField]));
283
+ const x = axes.toX(resolveX(item, i));
224
284
  const y = axes.toY(parseValue(item[props.yField]));
225
285
  if (i === 0) c.moveTo(x, y);
226
286
  else c.lineTo(x, y);
@@ -265,12 +325,12 @@ watch(panOffset, (v) => emit("pan-change", v), { deep: true });
265
325
  const hoveredPoints = computed(() => {
266
326
  if (hoveredIndex.value === -1) return [];
267
327
  const item = items.value[hoveredIndex.value];
268
- const xVal = parseValue(item[props.xField]);
328
+ const xVal = resolveX(item);
269
329
  const xPx = axes.toX(xVal);
270
330
  const pts = [];
271
331
  let seriesIdx = 0;
272
- for (const [key, seriesItems] of seriesMap.value) {
273
- const seriesItem = seriesItems.find((it) => parseValue(it[props.xField]) === xVal);
332
+ for (const [, seriesItems] of seriesMap.value) {
333
+ const seriesItem = seriesItems.find((it) => resolveX(it) === xVal);
274
334
  if (seriesItem) {
275
335
  pts.push({
276
336
  x: xPx,
@@ -303,7 +363,7 @@ const onMouseMove = (e) => {
303
363
  if (!rect) return;
304
364
  const mouseX = e.clientX - rect.left;
305
365
  const mouseY = e.clientY - rect.top;
306
- const nearest = findNearestX(items.value, mouseX, props.xField, axes);
366
+ const nearest = findNearestX(items.value, mouseX, props.xField, axes, (r, i) => resolveX(r, i));
307
367
  if (nearest) {
308
368
  setHovered(nearest.index);
309
369
  showTooltip(mouseX + 10, mouseY - 20, `${nearest.item[props.xField]}: ${nearest.item[props.yField]}`, nearest.item, nearest.index);
@@ -122,6 +122,34 @@ const {
122
122
  } = viewport;
123
123
  const xFieldRef = computed(() => props.xField);
124
124
  const yFieldRef = computed(() => props.yField);
125
+ const xAxisType = computed(() => {
126
+ if (!items.value.length) return "numeric";
127
+ const sample = items.value[0][props.xField];
128
+ if (typeof sample === "number") return "numeric";
129
+ if (typeof sample === "string") {
130
+ const ts = Date.parse(sample);
131
+ if (!isNaN(ts) && isNaN(Number(sample))) return "datetime";
132
+ return "category";
133
+ }
134
+ return "numeric";
135
+ });
136
+ const categoryList = computed(() => {
137
+ if (xAxisType.value !== "category") return [];
138
+ return [...new Set(items.value.map((r) => String(r[props.xField])))];
139
+ });
140
+ const categoryIndexMap = computed(() => {
141
+ const map = {};
142
+ categoryList.value.forEach((cat, i) => {
143
+ map[cat] = i;
144
+ });
145
+ return map;
146
+ });
147
+ const resolveX = (item, index = 0) => {
148
+ if (!item) return index;
149
+ const raw = item[props.xField];
150
+ if (xAxisType.value === "category") return categoryIndexMap.value[String(raw)] ?? index;
151
+ return parseValue(raw, index);
152
+ };
125
153
  const seriesMap = computed(() => {
126
154
  const map = /* @__PURE__ */ new Map();
127
155
  for (const item of items.value) {
@@ -139,7 +167,8 @@ const axes = useChartAxes({
139
167
  height,
140
168
  scale,
141
169
  panOffset,
142
- padding: { top: 20, right: 20, bottom: 40, left: 55 }
170
+ padding: { top: 20, right: 20, bottom: 40, left: 55 },
171
+ xValueGetter: (r, i) => resolveX(r, i)
143
172
  });
144
173
  const draw = () => {
145
174
  if (!ctx.value) return;
@@ -220,13 +249,36 @@ const drawAxes = (c) => {
220
249
  c.fillText(val.toFixed(1), axes.plotLeft - 8, y + 4);
221
250
  }
222
251
  c.textAlign = "center";
223
- const xSteps = 5;
224
- for (let i = 0; i <= xSteps; i++) {
225
- const val = axes.xMin.value + (axes.xMax.value - axes.xMin.value) * (i / xSteps);
226
- const x = axes.toX(val);
227
- let label = val.toFixed(1);
228
- if (val > 1e12) label = new Date(val).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
229
- c.fillText(label, x, axes.plotTop + axes.plotHeight.value + 16);
252
+ if (xAxisType.value === "category") {
253
+ const count = categoryList.value.length;
254
+ if (count > 0) {
255
+ const xSteps = Math.min(10, count - 1);
256
+ const stepSize = xSteps > 0 ? (count - 1) / xSteps : 1;
257
+ for (let i = 0; i <= xSteps; i++) {
258
+ const idx = Math.round(i * stepSize);
259
+ const x = axes.toX(idx);
260
+ const label = categoryList.value[idx] ?? "";
261
+ c.fillText(label, x, axes.plotTop + axes.plotHeight.value + 16);
262
+ }
263
+ }
264
+ } else {
265
+ const xSteps = 5;
266
+ for (let i = 0; i <= xSteps; i++) {
267
+ const val = axes.xMin.value + (axes.xMax.value - axes.xMin.value) * (i / xSteps);
268
+ const x = axes.toX(val);
269
+ let label;
270
+ if (xAxisType.value === "datetime") {
271
+ const allMidnight = items.value.every((r) => {
272
+ const ts = parseValue(r[props.xField]);
273
+ const d = new Date(ts);
274
+ return d.getHours() === 0 && d.getMinutes() === 0;
275
+ });
276
+ label = allMidnight ? new Date(val).toLocaleDateString([], { month: "short", day: "numeric" }) : new Date(val).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
277
+ } else {
278
+ label = val.toFixed(1);
279
+ }
280
+ c.fillText(label, x, axes.plotTop + axes.plotHeight.value + 16);
281
+ }
230
282
  }
231
283
  c.beginPath();
232
284
  c.moveTo(axes.plotLeft, axes.plotTop + axes.plotHeight.value);
@@ -253,7 +305,7 @@ const drawAxes = (c) => {
253
305
  };
254
306
  const drawLine = (c, data, color, progress2) => {
255
307
  if (!data.length) return;
256
- const sorted = [...data].sort((a, b) => parseValue(a[props.xField]) - parseValue(b[props.xField]));
308
+ const sorted = [...data].sort((a, b) => resolveX(a) - resolveX(b));
257
309
  const totalPts = sorted.length;
258
310
  const drawCount = Math.max(1, Math.floor(totalPts * progress2));
259
311
  c.save();
@@ -263,7 +315,7 @@ const drawLine = (c, data, color, progress2) => {
263
315
  c.lineCap = "round";
264
316
  c.beginPath();
265
317
  sorted.slice(0, drawCount).forEach((item, i) => {
266
- const x = axes.toX(parseValue(item[props.xField]));
318
+ const x = axes.toX(resolveX(item, i));
267
319
  const y = axes.toY(parseValue(item[props.yField]));
268
320
  if (i === 0) c.moveTo(x, y);
269
321
  else c.lineTo(x, y);
@@ -271,7 +323,7 @@ const drawLine = (c, data, color, progress2) => {
271
323
  c.stroke();
272
324
  for (let i = 0; i < drawCount; i++) {
273
325
  const item = sorted[i];
274
- const x = axes.toX(parseValue(item[props.xField]));
326
+ const x = axes.toX(resolveX(item, i));
275
327
  const y = axes.toY(parseValue(item[props.yField]));
276
328
  const isHovered = hoveredIndex.value === items.value.indexOf(item);
277
329
  c.beginPath();
@@ -301,12 +353,12 @@ onMounted(async () => {
301
353
  const hoveredPoints = computed(() => {
302
354
  if (hoveredIndex.value === -1) return [];
303
355
  const item = items.value[hoveredIndex.value];
304
- const xVal = parseValue(item[props.xField]);
356
+ const xVal = resolveX(item);
305
357
  const xPx = axes.toX(xVal);
306
358
  const pts = [];
307
359
  let seriesIdx = 0;
308
360
  for (const [key, seriesItems] of seriesMap.value) {
309
- const seriesItem = seriesItems.find((it) => parseValue(it[props.xField]) === xVal);
361
+ const seriesItem = seriesItems.find((it) => resolveX(it) === xVal);
310
362
  if (seriesItem) {
311
363
  pts.push({
312
364
  x: xPx,
@@ -331,7 +383,7 @@ const onMouseMove = (e) => {
331
383
  if (!rect) return;
332
384
  const mouseX = e.clientX - rect.left;
333
385
  const mouseY = e.clientY - rect.top;
334
- const nearest = findNearestX(items.value, mouseX, props.xField, axes);
386
+ const nearest = findNearestX(items.value, mouseX, props.xField, axes, (r, i) => resolveX(r, i));
335
387
  if (nearest) {
336
388
  setHovered(nearest.index);
337
389
  showTooltip(mouseX + 10, mouseY - 20, `${nearest.item[props.xField]}: ${nearest.item[props.yField]}`, nearest.item, nearest.index);
@@ -25,10 +25,9 @@
25
25
  :height="axes.plotHeight.value"
26
26
  )
27
27
  g.k-chart-hits
28
- // No circles here! Distance-based hit testing now.
29
28
 
30
29
  // Dynamic Interaction Layer
31
- g.k-chart-interaction(v-if="hoveredIndex !== -1 && !isLoading" clip-path="url(#plot-clip)")
30
+ g.k-chart-interaction(v-if="hoveredIndex !== -1 && !isLoading && hoveredPoint" clip-path="url(#plot-clip)")
32
31
  circle.k-chart-hit-proxy(
33
32
  :cx="hoveredPoint.x"
34
33
  :cy="hoveredPoint.y"
@@ -56,7 +55,7 @@ import { ref, watch, computed, onMounted, nextTick } from "vue";
56
55
  import { useChartCanvas } from "../../composables/useChartCanvas";
57
56
  import { useChartSvg } from "../../composables/useChartSvg";
58
57
  import { useChartData } from "../../composables/useChartData";
59
- import { useChartAxes } from "../../composables/useChartAxes";
58
+ import { useChartAxes, parseValue } from "../../composables/useChartAxes";
60
59
  import { useChartViewport } from "../../composables/useChartViewport";
61
60
  import { useChartAnimation, easeOut } from "../../composables/useChartAnimation";
62
61
  import { useChartExport } from "../../composables/useChartExport";
@@ -111,7 +110,45 @@ const {
111
110
  } = viewport;
112
111
  const xFieldRef = computed(() => props.xField);
113
112
  const yFieldRef = computed(() => props.yField);
114
- const axes = useChartAxes({ items, xField: xFieldRef, yField: yFieldRef, width, height, scale, panOffset, padding: { top: 20, right: 20, bottom: 40, left: 55 } });
113
+ const xAxisType = computed(() => {
114
+ if (!items.value.length) return "numeric";
115
+ const sample = items.value[0][props.xField];
116
+ if (typeof sample === "number") return "numeric";
117
+ if (typeof sample === "string") {
118
+ const ts = Date.parse(sample);
119
+ if (!isNaN(ts) && isNaN(Number(sample))) return "datetime";
120
+ return "category";
121
+ }
122
+ return "numeric";
123
+ });
124
+ const categoryList = computed(() => {
125
+ if (xAxisType.value !== "category") return [];
126
+ return [...new Set(items.value.map((r) => String(r[props.xField])))];
127
+ });
128
+ const categoryIndexMap = computed(() => {
129
+ const map = {};
130
+ categoryList.value.forEach((cat, i) => {
131
+ map[cat] = i;
132
+ });
133
+ return map;
134
+ });
135
+ const resolveX = (item, index = 0) => {
136
+ if (!item) return index;
137
+ const raw = item[props.xField];
138
+ if (xAxisType.value === "category") return categoryIndexMap.value[String(raw)] ?? index;
139
+ return parseValue(raw, index);
140
+ };
141
+ const axes = useChartAxes({
142
+ items,
143
+ xField: xFieldRef,
144
+ yField: yFieldRef,
145
+ width,
146
+ height,
147
+ scale,
148
+ panOffset,
149
+ padding: { top: 20, right: 20, bottom: 40, left: 55 },
150
+ xValueGetter: (r, i) => resolveX(r, i)
151
+ });
115
152
  const maxSize = computed(() => Math.max(...items.value.map((r) => Number(r[props.sizeField] ?? 1)), 1));
116
153
  const seriesMap = computed(() => {
117
154
  const map = /* @__PURE__ */ new Map();
@@ -125,6 +162,7 @@ const seriesMap = computed(() => {
125
162
  const hoveredPoint = computed(() => {
126
163
  if (hoveredIndex.value === -1) return null;
127
164
  const item = items.value[hoveredIndex.value];
165
+ if (!item) return null;
128
166
  const sizeRaw = Number(item[props.sizeField] ?? 1);
129
167
  const r = 4 + sizeRaw / maxSize.value * 16;
130
168
  const seriesKey = String(item[props.seriesField] ?? "default");
@@ -132,8 +170,8 @@ const hoveredPoint = computed(() => {
132
170
  const seriesIdx = seriesKeys.indexOf(seriesKey);
133
171
  const color = props.colors[seriesIdx % props.colors.length];
134
172
  return {
135
- x: axes.toX(Number(item[props.xField])),
136
- y: axes.toY(Number(item[props.yField])),
173
+ x: axes.toX(resolveX(item, hoveredIndex.value)),
174
+ y: axes.toY(parseValue(item[props.yField])),
137
175
  r,
138
176
  color,
139
177
  item,
@@ -179,9 +217,35 @@ const draw = () => {
179
217
  c.fillText(val.toFixed(1), axes.plotLeft - 8, axes.toY(val) + 4);
180
218
  }
181
219
  c.textAlign = "center";
182
- for (let i = 0; i <= 5; i++) {
183
- const val = axes.xMin.value + (axes.xMax.value - axes.xMin.value) * (i / 5);
184
- c.fillText(val.toFixed(1), axes.toX(val), axes.plotTop + axes.plotHeight.value + 16);
220
+ if (xAxisType.value === "category") {
221
+ const count = categoryList.value.length;
222
+ if (count > 0) {
223
+ const xSteps = Math.min(10, count - 1);
224
+ const stepSize = xSteps > 0 ? (count - 1) / xSteps : 1;
225
+ for (let i = 0; i <= xSteps; i++) {
226
+ const idx = Math.round(i * stepSize);
227
+ const x = axes.toX(idx);
228
+ const label = categoryList.value[idx] ?? "";
229
+ c.fillText(label, x, axes.plotTop + axes.plotHeight.value + 16);
230
+ }
231
+ }
232
+ } else {
233
+ for (let i = 0; i <= 5; i++) {
234
+ const val = axes.xMin.value + (axes.xMax.value - axes.xMin.value) * (i / 5);
235
+ const x = axes.toX(val);
236
+ let label;
237
+ if (xAxisType.value === "datetime") {
238
+ const allMidnight = items.value.every((r) => {
239
+ const ts = parseValue(r[props.xField]);
240
+ const d = new Date(ts);
241
+ return d.getHours() === 0 && d.getMinutes() === 0;
242
+ });
243
+ label = allMidnight ? new Date(val).toLocaleDateString([], { month: "short", day: "numeric" }) : new Date(val).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
244
+ } else {
245
+ label = val.toFixed(1);
246
+ }
247
+ c.fillText(label, x, axes.toX(val), axes.plotTop + axes.plotHeight.value + 16);
248
+ }
185
249
  }
186
250
  c.fillStyle = "rgba(120,120,140,0.8)";
187
251
  if (props.xAxisTitle) {
@@ -204,8 +268,8 @@ const draw = () => {
204
268
  for (const [, seriesItems] of seriesMap.value) {
205
269
  const color = props.colors[seriesIdx % props.colors.length];
206
270
  seriesItems.forEach((item) => {
207
- const x = axes.toX(Number(item[props.xField]));
208
- const y = axes.toY(Number(item[props.yField]));
271
+ const x = axes.toX(resolveX(item, items.value.indexOf(item)));
272
+ const y = axes.toY(parseValue(item[props.yField]));
209
273
  const sizeRaw = Number(item[props.sizeField] ?? 1);
210
274
  const r = 4 + sizeRaw / maxSize.value * 16;
211
275
  const isHovered = items.value[hoveredIndex.value] === item;
@@ -18,6 +18,7 @@ export interface IChartAxesOptions {
18
18
  left: number;
19
19
  };
20
20
  scaleMode?: TScaleMode;
21
+ xValueGetter?: (item: any, index: number) => number;
21
22
  }
22
23
  export interface IChartAxesContext {
23
24
  xMin: ComputedRef<number>;
@@ -16,12 +16,16 @@ export function useChartAxes(opts) {
16
16
  const plotTop = pad.top;
17
17
  const plotWidth = computed(() => Math.max(0, opts.width.value - pad.left - pad.right));
18
18
  const plotHeight = computed(() => Math.max(0, opts.height.value - pad.top - pad.bottom));
19
+ const getXVal = (r, i) => {
20
+ if (opts.xValueGetter) return opts.xValueGetter(r, i);
21
+ return parseValue(r[opts.xField.value], i);
22
+ };
19
23
  const rawXMin = computed(() => {
20
- const vals = opts.items.value.map((r, i) => parseValue(r[opts.xField.value], i));
24
+ const vals = opts.items.value.map((r, i) => getXVal(r, i));
21
25
  return vals.length ? Math.min(...vals) : 0;
22
26
  });
23
27
  const rawXMax = computed(() => {
24
- const vals = opts.items.value.map((r, i) => parseValue(r[opts.xField.value], i));
28
+ const vals = opts.items.value.map((r, i) => getXVal(r, i));
25
29
  return vals.length ? Math.max(...vals) : 1;
26
30
  });
27
31
  const yMin = computed(() => {
@@ -1,6 +1,6 @@
1
1
  import { type IChartAxesContext } from './useChartAxes.js';
2
2
  export declare function useChartHitTest(): {
3
- findNearestX: (items: any[], mouseX: number, xField: string, axes: IChartAxesContext) => {
3
+ findNearestX: (items: any[], mouseX: number, xField: string, axes: IChartAxesContext, xValueGetter?: (item: any, index?: number) => number) => {
4
4
  item: any;
5
5
  index: number;
6
6
  } | null;
@@ -1,12 +1,13 @@
1
1
  import { parseValue } from "./useChartAxes.js";
2
2
  export function useChartHitTest() {
3
- const findNearestX = (items, mouseX, xField, axes) => {
3
+ const findNearestX = (items, mouseX, xField, axes, xValueGetter) => {
4
4
  if (!items.length) return null;
5
5
  const dataX = axes.fromX(mouseX);
6
+ const getX = xValueGetter ?? ((item, i) => parseValue(item[xField], i));
6
7
  let nearestIdx = 0;
7
- let minDiff = Math.abs(parseValue(items[0][xField]) - dataX);
8
+ let minDiff = Math.abs(getX(items[0], 0) - dataX);
8
9
  for (let i = 1; i < items.length; i++) {
9
- const val = parseValue(items[i][xField]);
10
+ const val = getX(items[i], i);
10
11
  const diff = Math.abs(val - dataX);
11
12
  if (diff < minDiff) {
12
13
  minDiff = diff;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@katlux/block-charts",
3
- "version": "0.1.1-alpha.87",
3
+ "version": "0.1.1-alpha.88",
4
4
  "description": "Pre-built charting block components for Katlux toolkit",
5
5
  "author": "Katlux",
6
6
  "license": "Apache-2.0",