|
157 | 157 | const y = $derived(accessor(yProp)); |
158 | 158 |
|
159 | 159 | const highlightData = $derived(data ?? ctx.tooltip.data); |
| 160 | +
|
160 | 161 | const xValue = $derived(x(highlightData)); |
161 | 162 | const xCoord = $derived( |
162 | 163 | Array.isArray(xValue) ? xValue.map((v) => ctx.xScale(v)) : ctx.xScale(xValue) |
163 | 164 | ); |
164 | 165 | const xOffset = $derived(isScaleBand(ctx.xScale) && !ctx.radial ? ctx.xScale.bandwidth() / 2 : 0); |
| 166 | +
|
165 | 167 | const yValue = $derived(y(highlightData)); |
166 | 168 | const yCoord = $derived( |
167 | 169 | Array.isArray(yValue) ? yValue.map((v) => ctx.yScale(v)) : ctx.yScale(yValue) |
168 | 170 | ); |
169 | 171 | const yOffset = $derived(isScaleBand(ctx.yScale) && !ctx.radial ? ctx.yScale.bandwidth() / 2 : 0); |
| 172 | +
|
170 | 173 | const axis = $derived( |
171 | 174 | axisProp == null ? (isScaleBand(ctx.yScale) || isScaleTime(ctx.yScale) ? 'y' : 'x') : axisProp |
172 | 175 | ); |
|
324 | 327 | return tmpArea; |
325 | 328 | }); |
326 | 329 |
|
327 | | - // Helper to find series info from tooltipState.series by key |
328 | | - function getTooltipSeries(key: string) { |
329 | | - return ctx.tooltip.series.find((s) => s.key === key); |
330 | | - } |
331 | | -
|
332 | 330 | const _points: HighlightPoint[] = $derived.by(() => { |
333 | 331 | let tmpPoints: HighlightPoint[] = []; |
334 | 332 | if (!highlightData) return tmpPoints; |
335 | 333 |
|
336 | | - const tooltipSeries = ctx.tooltip.series; |
337 | | -
|
338 | | - if (Array.isArray(xCoord)) { |
339 | | - // `x` accessor with multiple properties (ex. `x={['start', 'end']}` or `x={[0, 1]}`) |
| 334 | + // Use tooltip.series directly when: |
| 335 | + // 1. No explicit data prop (using ctx.tooltip.data) |
| 336 | + // 2. tooltip.series is populated |
| 337 | + // This handles multi-series charts where a single Highlight renders all series points |
| 338 | + if (data === undefined && ctx.tooltip.series.length > 0) { |
| 339 | + tmpPoints = ctx.tooltip.series |
| 340 | + .map((seriesInfo) => { |
| 341 | + // Skip if series is not visible |
| 342 | + if (!seriesInfo.visible) return null; |
| 343 | +
|
| 344 | + let pointX: number; |
| 345 | + let pointY: number; |
| 346 | + let dataX: any; |
| 347 | + let dataY: any; |
| 348 | +
|
| 349 | + // For stacked charts, use getStackValue to get the cumulative y1 value |
| 350 | + if (ctx.series.isStacked) { |
| 351 | + // Find the matching data point in flatData by comparing x values |
| 352 | + // This is needed because tooltip.data might be a different object reference |
| 353 | + const matchingData = ctx.flatData.find((d) => x(d) === xValue); |
| 354 | + const stackValue = matchingData |
| 355 | + ? ctx.series.getStackValue(seriesInfo.key, matchingData) |
| 356 | + : null; |
| 357 | + const stackedY1 = stackValue?.[1] ?? 0; |
| 358 | +
|
| 359 | + if (ctx.valueAxis === 'x') { |
| 360 | + // Horizontal stacked chart |
| 361 | + pointX = ctx.xScale(stackedY1) + xOffset; |
| 362 | + pointY = (yCoord as number) + yOffset; |
| 363 | + dataX = stackedY1; |
| 364 | + dataY = yValue; |
| 365 | + } else { |
| 366 | + // Vertical stacked chart (default) |
| 367 | + pointX = (xCoord as number) + xOffset; |
| 368 | + pointY = ctx.yScale(stackedY1) + yOffset; |
| 369 | + dataX = xValue; |
| 370 | + dataY = stackedY1; |
| 371 | + } |
| 372 | + } else { |
| 373 | + // Non-stacked charts - use tooltip.series value directly |
| 374 | + const seriesValue = seriesInfo.value; |
| 375 | +
|
| 376 | + if (ctx.valueAxis === 'x') { |
| 377 | + // Horizontal chart - value is on x-axis |
| 378 | + pointX = ctx.xScale(seriesValue) + xOffset; |
| 379 | + pointY = (yCoord as number) + yOffset; |
| 380 | + dataX = seriesValue; |
| 381 | + dataY = yValue; |
| 382 | + } else { |
| 383 | + // Vertical chart (default) - value is on y-axis |
| 384 | + pointX = (xCoord as number) + xOffset; |
| 385 | + pointY = ctx.yScale(seriesValue) + yOffset; |
| 386 | + dataX = xValue; |
| 387 | + dataY = seriesValue; |
| 388 | + } |
| 389 | + } |
| 390 | +
|
| 391 | + return { |
| 392 | + x: pointX, |
| 393 | + y: pointY, |
| 394 | + fill: seriesInfo.color ?? '', |
| 395 | + data: { |
| 396 | + x: dataX, |
| 397 | + y: dataY, |
| 398 | + }, |
| 399 | + seriesKey: seriesInfo.key, |
| 400 | + }; |
| 401 | + }) |
| 402 | + .filter(notNull); |
| 403 | + } else if (Array.isArray(xCoord)) { |
| 404 | + // Fallback: `x` accessor with multiple properties (ex. `x={['start', 'end']}` or `x={[0, 1]}`) |
340 | 405 |
|
341 | 406 | if (Array.isArray(highlightData)) { |
342 | 407 | // Stack series (ex. `y={[['apples', 'bananas', 'oranges']]})`) |
|
357 | 422 |
|
358 | 423 | tmpPoints = seriesPointsData |
359 | 424 | .map((seriesPoint, i) => { |
360 | | - const seriesInfo = tooltipSeries[i]; |
361 | | - // Skip if series is not visible |
362 | | - if (seriesInfo && !seriesInfo.visible) return null; |
363 | | -
|
364 | 425 | // Use tooltipState.series color if available, fallback to ctx.cGet |
365 | | - const fill = |
366 | | - seriesInfo?.color ?? (ctx.config.c ? ctx.cGet(seriesPoint.series) : null); |
| 426 | + const fill = ctx.config.c ? ctx.cGet(seriesPoint.series) : null; |
367 | 427 |
|
368 | 428 | return { |
369 | 429 | x: ctx.xScale(seriesPoint.point[1]) + xOffset, |
|
373 | 433 | x: seriesPoint.point[1], |
374 | 434 | y: yValue, |
375 | 435 | }, |
376 | | - seriesKey: seriesInfo?.key, |
| 436 | + seriesKey: undefined, |
377 | 437 | }; |
378 | 438 | }) |
379 | 439 | .filter(notNull); |
|
385 | 445 | if (xItem == null) return null; |
386 | 446 | // @ts-expect-error - TODO: fix type |
387 | 447 | const _key = ctx.config.x?.[i]; |
388 | | - const seriesInfo = getTooltipSeries(_key); |
389 | 448 |
|
390 | | - // Skip if series is not visible |
391 | | - if (seriesInfo && !seriesInfo.visible) return null; |
392 | | -
|
393 | | - // Use tooltipState.series color if available |
394 | | - const fill = |
395 | | - seriesInfo?.color ?? |
396 | | - (ctx.config.c ? ctx.cGet({ ...highlightData, $key: _key }) : null); |
| 449 | + const fill = ctx.config.c ? ctx.cGet({ ...highlightData, $key: _key }) : null; |
397 | 450 |
|
398 | 451 | return { |
399 | 452 | x: xItem + xOffset, |
|
409 | 462 | .filter(notNull); |
410 | 463 | } |
411 | 464 | } else if (Array.isArray(yCoord)) { |
412 | | - // `y` accessor with multiple properties (ex. `y={['apples', 'bananas', 'oranges']}` or `y={[0, 1]}) |
| 465 | + // Fallback: `y` accessor with multiple properties (ex. `y={['apples', 'bananas', 'oranges']}` or `y={[0, 1]}) |
413 | 466 |
|
414 | 467 | if (Array.isArray(highlightData)) { |
415 | 468 | // Stack series (ex. `y={[['apples', 'bananas', 'oranges']]})`) |
|
430 | 483 |
|
431 | 484 | tmpPoints = seriesPointsData |
432 | 485 | .map((seriesPoint, i) => { |
433 | | - const seriesInfo = tooltipSeries[i]; |
434 | | - // Skip if series is not visible |
435 | | - if (seriesInfo && !seriesInfo.visible) return null; |
436 | | -
|
437 | 486 | // Use tooltipState.series color if available, fallback to ctx.cGet |
438 | | - const fill = |
439 | | - seriesInfo?.color ?? (ctx.config.c ? ctx.cGet(seriesPoint.series) : null); |
| 487 | + const fill = ctx.config.c ? ctx.cGet(seriesPoint.series) : null; |
440 | 488 |
|
441 | 489 | return { |
442 | 490 | x: xCoord + xOffset, |
|
446 | 494 | x: xValue, |
447 | 495 | y: seriesPoint.point[1], |
448 | 496 | }, |
449 | | - seriesKey: seriesInfo?.key, |
| 497 | + seriesKey: undefined, |
450 | 498 | }; |
451 | 499 | }) |
452 | 500 | .filter(notNull); |
|
458 | 506 | if (yItem == null) return null; |
459 | 507 | // @ts-expect-error - TODO: fix type |
460 | 508 | const _key = ctx.config.y[i]; |
461 | | - const seriesInfo = getTooltipSeries(_key); |
462 | | -
|
463 | | - // Skip if series is not visible |
464 | | - if (seriesInfo && !seriesInfo.visible) return null; |
465 | 509 |
|
466 | | - // Use tooltipState.series color if available |
467 | | - const fill = |
468 | | - seriesInfo?.color ?? |
469 | | - (ctx.config.c ? ctx.cGet({ ...highlightData, $key: _key }) : null); |
| 510 | + const fill = ctx.config.c ? ctx.cGet({ ...highlightData, $key: _key }) : null; |
470 | 511 |
|
471 | 512 | return { |
472 | 513 | x: xCoord + xOffset, |
|
482 | 523 | .filter(notNull); |
483 | 524 | } |
484 | 525 | } else if (xCoord != null && yCoord != null) { |
485 | | - // Single point - use tooltipState.series if available (for single-series charts) |
486 | | - const seriesInfo = tooltipSeries.length === 1 ? tooltipSeries[0] : null; |
487 | | - const fill = seriesInfo?.color ?? (ctx.config.c ? ctx.cGet(highlightData) : null); |
| 526 | + // Fallback: Single point without tooltip.series |
| 527 | + const fill = ctx.config.c ? ctx.cGet(highlightData) : null; |
488 | 528 |
|
489 | 529 | tmpPoints = [ |
490 | 530 | { |
|
495 | 535 | x: xValue, |
496 | 536 | y: yValue, |
497 | 537 | }, |
498 | | - seriesKey: seriesInfo?.key, |
| 538 | + seriesKey: undefined, |
499 | 539 | }, |
500 | 540 | ]; |
501 | 541 | } else { |
|
0 commit comments