@cfasim-ui/docs 0.5.0 → 0.6.0

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.
@@ -286,6 +286,10 @@ const props = withDefaults(defineProps<BarChartProps>(), {
286
286
  valueAxis: true,
287
287
  });
288
288
 
289
+ // The template root is a <Teleport>, so fallthrough attrs (class, style,
290
+ // data-*, id…) can't auto-inherit — forward them onto the wrapper manually.
291
+ defineOptions({ inheritAttrs: false });
292
+
289
293
  const emit = defineEmits<{
290
294
  (e: "hover", payload: ChartHoverPayload): void;
291
295
  }>();
@@ -1004,6 +1008,9 @@ const {
1004
1008
  triggerCsvDownload,
1005
1009
  menuFilename,
1006
1010
  isFullscreen,
1011
+ fullscreenStyle,
1012
+ teleportTarget,
1013
+ exitFullscreen,
1007
1014
  } = useChartFoundation({
1008
1015
  width: () => props.width,
1009
1016
  height: () => props.height,
@@ -1018,11 +1025,15 @@ const {
1018
1025
  filename: () => props.filename,
1019
1026
  downloadLink: () => props.downloadLink,
1020
1027
  downloadButton: () => props.downloadButton,
1028
+ fullscreenTarget: () => props.fullscreenTarget,
1021
1029
  chartPadding: () => effectiveChartPadding.value,
1022
1030
  inlineLegendLabels: () => inlineLegendLabels.value,
1023
1031
  hasTooltipSlot: () => hasTooltipSlot.value,
1024
1032
  getCsv: toCsv,
1025
1033
  pointerToIndex,
1034
+ // Bars run along the category axis: vertical orientation scrubs across X
1035
+ // (keep vertical page scroll), horizontal orientation scrubs down Y.
1036
+ scrubAxis: () => (isVertical.value ? "x" : "y"),
1026
1037
  onHover: (payload) => emit("hover", payload),
1027
1038
  });
1028
1039
 
@@ -1457,349 +1468,363 @@ const columnHeaders = computed<ColumnHeader[]>(() => {
1457
1468
  </script>
1458
1469
 
1459
1470
  <template>
1460
- <div
1461
- ref="containerRef"
1462
- class="bar-chart-wrapper"
1463
- :class="{ 'is-fullscreen': isFullscreen }"
1464
- >
1465
- <ChartMenu v-if="menu" :items="menuItems" />
1466
- <div class="chart-sr-only" aria-live="polite">
1467
- {{ isFullscreen ? "Chart expanded to fill window" : "" }}
1468
- </div>
1469
- <svg ref="svgRef" :width="width" :height="height">
1470
- <!-- title -->
1471
- <text
1472
- v-if="title"
1473
- :x="titleResolved.x"
1474
- :y="titleResolved.lineHeight"
1475
- :text-anchor="titleResolved.anchor"
1476
- :font-size="titleResolved.fontSize"
1477
- :font-weight="titleResolved.fontWeight"
1478
- :fill="titleResolved.color"
1479
- >
1480
- <tspan
1481
- v-for="(line, i) in titleResolved.lines"
1482
- :key="i"
1471
+ <Teleport :to="teleportTarget" :disabled="!isFullscreen">
1472
+ <div
1473
+ ref="containerRef"
1474
+ v-bind="$attrs"
1475
+ class="bar-chart-wrapper"
1476
+ :class="{ 'is-fullscreen': isFullscreen }"
1477
+ :style="fullscreenStyle"
1478
+ >
1479
+ <ChartMenu
1480
+ v-if="menu"
1481
+ :items="menuItems"
1482
+ :is-fullscreen="isFullscreen"
1483
+ @close="exitFullscreen"
1484
+ />
1485
+ <div class="chart-sr-only" aria-live="polite">
1486
+ {{ isFullscreen ? "Chart expanded to fill window" : "" }}
1487
+ </div>
1488
+ <svg ref="svgRef" :width="width" :height="height">
1489
+ <!-- title -->
1490
+ <text
1491
+ v-if="title"
1483
1492
  :x="titleResolved.x"
1484
- :dy="i === 0 ? 0 : titleResolved.lineHeight"
1493
+ :y="titleResolved.lineHeight"
1494
+ :text-anchor="titleResolved.anchor"
1495
+ :font-size="titleResolved.fontSize"
1496
+ :font-weight="titleResolved.fontWeight"
1497
+ :fill="titleResolved.color"
1485
1498
  >
1486
- {{ line }}
1487
- </tspan>
1488
- </text>
1489
- <!-- column headers (category / value), in the reserved row above the plot -->
1490
- <text
1491
- v-for="h in columnHeaders"
1492
- :key="h.key"
1493
- :data-testid="h.key === 'ch' ? 'category-header' : 'value-header'"
1494
- :x="h.x"
1495
- :y="h.y"
1496
- :text-anchor="h.anchor"
1497
- :font-size="axisLabelResolved.fontSize"
1498
- :fill="axisLabelResolved.fill"
1499
- font-weight="600"
1500
- >
1501
- {{ h.text }}
1502
- </text>
1503
- <!-- inline legend -->
1504
- <g v-if="positionedLegendItems.length > 0">
1505
- <template v-for="(item, i) in positionedLegendItems" :key="'ileg' + i">
1506
- <rect
1507
- v-if="item.kind === 'bar'"
1508
- :x="item.x"
1509
- :y="item.y - 5"
1510
- width="12"
1511
- height="10"
1512
- :fill="item.color"
1513
- />
1499
+ <tspan
1500
+ v-for="(line, i) in titleResolved.lines"
1501
+ :key="i"
1502
+ :x="titleResolved.x"
1503
+ :dy="i === 0 ? 0 : titleResolved.lineHeight"
1504
+ >
1505
+ {{ line }}
1506
+ </tspan>
1507
+ </text>
1508
+ <!-- column headers (category / value), in the reserved row above the plot -->
1509
+ <text
1510
+ v-for="h in columnHeaders"
1511
+ :key="h.key"
1512
+ :data-testid="h.key === 'ch' ? 'category-header' : 'value-header'"
1513
+ :x="h.x"
1514
+ :y="h.y"
1515
+ :text-anchor="h.anchor"
1516
+ :font-size="axisLabelResolved.fontSize"
1517
+ :fill="axisLabelResolved.fill"
1518
+ font-weight="600"
1519
+ >
1520
+ {{ h.text }}
1521
+ </text>
1522
+ <!-- inline legend -->
1523
+ <g v-if="positionedLegendItems.length > 0">
1524
+ <template
1525
+ v-for="(item, i) in positionedLegendItems"
1526
+ :key="'ileg' + i"
1527
+ >
1528
+ <rect
1529
+ v-if="item.kind === 'bar'"
1530
+ :x="item.x"
1531
+ :y="item.y - 5"
1532
+ width="12"
1533
+ height="10"
1534
+ :fill="item.color"
1535
+ />
1536
+ <line
1537
+ v-else
1538
+ :x1="item.x"
1539
+ :y1="item.y"
1540
+ :x2="item.x + 12"
1541
+ :y2="item.y"
1542
+ :stroke="item.color"
1543
+ stroke-width="2"
1544
+ :stroke-dasharray="item.dashed ? '4 2' : undefined"
1545
+ />
1546
+ <text
1547
+ :x="item.x + 18"
1548
+ :y="item.y + 4"
1549
+ :font-size="legendResolved.fontSize"
1550
+ :fill="legendResolved.fill"
1551
+ :font-weight="legendResolved.fontWeight"
1552
+ >
1553
+ {{ item.label }}
1554
+ </text>
1555
+ </template>
1556
+ </g>
1557
+ <!-- axes (the value axis line is suppressed when valueAxis is false) -->
1558
+ <line
1559
+ v-if="!isVertical || valueAxis"
1560
+ :x1="snap(padding.left)"
1561
+ :y1="snap(padding.top)"
1562
+ :x2="snap(padding.left)"
1563
+ :y2="snap(padding.top + innerH)"
1564
+ stroke="currentColor"
1565
+ stroke-opacity="0.3"
1566
+ />
1567
+ <line
1568
+ v-if="isVertical || valueAxis"
1569
+ :x1="snap(padding.left)"
1570
+ :y1="snap(padding.top + innerH)"
1571
+ :x2="snap(padding.left + innerW)"
1572
+ :y2="snap(padding.top + innerH)"
1573
+ stroke="currentColor"
1574
+ stroke-opacity="0.3"
1575
+ />
1576
+ <!-- value grid lines -->
1577
+ <template v-if="valueGrid && valueAxis">
1514
1578
  <line
1515
- v-else
1516
- :x1="item.x"
1517
- :y1="item.y"
1518
- :x2="item.x + 12"
1519
- :y2="item.y"
1520
- :stroke="item.color"
1521
- stroke-width="2"
1522
- :stroke-dasharray="item.dashed ? '4 2' : undefined"
1579
+ v-for="(tick, i) in valueTickItems"
1580
+ :key="'vg' + i"
1581
+ :x1="isVertical ? padding.left : tick.pos"
1582
+ :y1="isVertical ? tick.pos : padding.top"
1583
+ :x2="isVertical ? padding.left + innerW : tick.pos"
1584
+ :y2="isVertical ? tick.pos : padding.top + innerH"
1585
+ stroke="currentColor"
1586
+ stroke-opacity="0.1"
1523
1587
  />
1588
+ </template>
1589
+ <!-- hover highlight band (rendered behind bars) -->
1590
+ <rect
1591
+ v-if="hoverBand && hasTooltipSlot"
1592
+ :x="hoverBand.x"
1593
+ :y="hoverBand.y"
1594
+ :width="hoverBand.w"
1595
+ :height="hoverBand.h"
1596
+ fill="currentColor"
1597
+ fill-opacity="0.06"
1598
+ pointer-events="none"
1599
+ />
1600
+ <!-- value tick labels -->
1601
+ <template v-if="valueAxis && isVertical">
1524
1602
  <text
1525
- :x="item.x + 18"
1526
- :y="item.y + 4"
1527
- :font-size="legendResolved.fontSize"
1528
- :fill="legendResolved.fill"
1529
- :font-weight="legendResolved.fontWeight"
1603
+ v-for="(tick, i) in valueTickItems"
1604
+ :key="'vt' + i"
1605
+ data-testid="value-tick"
1606
+ :x="padding.left - 6"
1607
+ :y="tick.pos"
1608
+ text-anchor="end"
1609
+ dominant-baseline="middle"
1610
+ :font-size="tickLabelResolved.fontSize"
1611
+ :fill="tickLabelResolved.fill"
1612
+ :font-weight="tickLabelResolved.fontWeight"
1613
+ :fill-opacity="tickLabelResolved.fillOpacity"
1530
1614
  >
1531
- {{ item.label }}
1615
+ {{ tick.value }}
1532
1616
  </text>
1533
1617
  </template>
1534
- </g>
1535
- <!-- axes (the value axis line is suppressed when valueAxis is false) -->
1536
- <line
1537
- v-if="!isVertical || valueAxis"
1538
- :x1="snap(padding.left)"
1539
- :y1="snap(padding.top)"
1540
- :x2="snap(padding.left)"
1541
- :y2="snap(padding.top + innerH)"
1542
- stroke="currentColor"
1543
- stroke-opacity="0.3"
1544
- />
1545
- <line
1546
- v-if="isVertical || valueAxis"
1547
- :x1="snap(padding.left)"
1548
- :y1="snap(padding.top + innerH)"
1549
- :x2="snap(padding.left + innerW)"
1550
- :y2="snap(padding.top + innerH)"
1551
- stroke="currentColor"
1552
- stroke-opacity="0.3"
1553
- />
1554
- <!-- value grid lines -->
1555
- <template v-if="valueGrid && valueAxis">
1556
- <line
1557
- v-for="(tick, i) in valueTickItems"
1558
- :key="'vg' + i"
1559
- :x1="isVertical ? padding.left : tick.pos"
1560
- :y1="isVertical ? tick.pos : padding.top"
1561
- :x2="isVertical ? padding.left + innerW : tick.pos"
1562
- :y2="isVertical ? tick.pos : padding.top + innerH"
1563
- stroke="currentColor"
1564
- stroke-opacity="0.1"
1565
- />
1566
- </template>
1567
- <!-- hover highlight band (rendered behind bars) -->
1568
- <rect
1569
- v-if="hoverBand && hasTooltipSlot"
1570
- :x="hoverBand.x"
1571
- :y="hoverBand.y"
1572
- :width="hoverBand.w"
1573
- :height="hoverBand.h"
1574
- fill="currentColor"
1575
- fill-opacity="0.06"
1576
- pointer-events="none"
1577
- />
1578
- <!-- value tick labels -->
1579
- <template v-if="valueAxis && isVertical">
1580
- <text
1581
- v-for="(tick, i) in valueTickItems"
1582
- :key="'vt' + i"
1583
- data-testid="value-tick"
1584
- :x="padding.left - 6"
1585
- :y="tick.pos"
1586
- text-anchor="end"
1587
- dominant-baseline="middle"
1588
- :font-size="tickLabelResolved.fontSize"
1589
- :fill="tickLabelResolved.fill"
1590
- :font-weight="tickLabelResolved.fontWeight"
1591
- :fill-opacity="tickLabelResolved.fillOpacity"
1592
- >
1593
- {{ tick.value }}
1594
- </text>
1595
- </template>
1596
- <template v-else-if="valueAxis">
1618
+ <template v-else-if="valueAxis">
1619
+ <text
1620
+ v-for="(tick, i) in valueTickItems"
1621
+ :key="'vt' + i"
1622
+ data-testid="value-tick"
1623
+ :x="tick.pos"
1624
+ :y="padding.top + innerH + 16"
1625
+ text-anchor="middle"
1626
+ :font-size="tickLabelResolved.fontSize"
1627
+ :fill="tickLabelResolved.fill"
1628
+ :font-weight="tickLabelResolved.fontWeight"
1629
+ :fill-opacity="tickLabelResolved.fillOpacity"
1630
+ >
1631
+ {{ tick.value }}
1632
+ </text>
1633
+ </template>
1634
+ <!-- y axis label -->
1597
1635
  <text
1598
- v-for="(tick, i) in valueTickItems"
1599
- :key="'vt' + i"
1600
- data-testid="value-tick"
1601
- :x="tick.pos"
1602
- :y="padding.top + innerH + 16"
1636
+ v-if="yLabel"
1637
+ :x="0"
1638
+ :y="0"
1639
+ :transform="`translate(14, ${padding.top + innerH / 2}) rotate(-90)`"
1603
1640
  text-anchor="middle"
1604
- :font-size="tickLabelResolved.fontSize"
1605
- :fill="tickLabelResolved.fill"
1606
- :font-weight="tickLabelResolved.fontWeight"
1607
- :fill-opacity="tickLabelResolved.fillOpacity"
1641
+ :font-size="axisLabelResolved.fontSize"
1642
+ :fill="axisLabelResolved.fill"
1643
+ :font-weight="axisLabelResolved.fontWeight"
1608
1644
  >
1609
- {{ tick.value }}
1645
+ {{ yLabel }}
1610
1646
  </text>
1611
- </template>
1612
- <!-- y axis label -->
1613
- <text
1614
- v-if="yLabel"
1615
- :x="0"
1616
- :y="0"
1617
- :transform="`translate(14, ${padding.top + innerH / 2}) rotate(-90)`"
1618
- text-anchor="middle"
1619
- :font-size="axisLabelResolved.fontSize"
1620
- :fill="axisLabelResolved.fill"
1621
- :font-weight="axisLabelResolved.fontWeight"
1622
- >
1623
- {{ yLabel }}
1624
- </text>
1625
- <!-- category tick labels -->
1626
- <template v-if="isVertical">
1647
+ <!-- category tick labels -->
1648
+ <template v-if="isVertical">
1649
+ <text
1650
+ v-for="(tick, i) in categoryTickItems"
1651
+ :key="'ct' + i"
1652
+ data-testid="category-tick"
1653
+ :x="tick.pos"
1654
+ :y="padding.top + innerH + 16"
1655
+ :text-anchor="tick.anchor"
1656
+ :font-size="tickLabelResolved.fontSize"
1657
+ :fill="tickLabelResolved.fill"
1658
+ :font-weight="tickLabelResolved.fontWeight"
1659
+ :fill-opacity="tickLabelResolved.fillOpacity"
1660
+ >
1661
+ {{ tick.label }}
1662
+ </text>
1663
+ </template>
1664
+ <template v-else>
1665
+ <text
1666
+ v-for="(tick, i) in categoryTickItems"
1667
+ :key="'ct' + i"
1668
+ data-testid="category-tick"
1669
+ :x="categoryLabelLayout.x"
1670
+ :y="tick.pos"
1671
+ :text-anchor="categoryLabelLayout.anchor"
1672
+ dominant-baseline="middle"
1673
+ :font-size="tickLabelResolved.fontSize"
1674
+ :fill="tickLabelResolved.fill"
1675
+ :font-weight="tickLabelResolved.fontWeight"
1676
+ :fill-opacity="tickLabelResolved.fillOpacity"
1677
+ >
1678
+ {{ tick.label }}
1679
+ </text>
1680
+ </template>
1681
+ <!-- x axis label -->
1627
1682
  <text
1628
- v-for="(tick, i) in categoryTickItems"
1629
- :key="'ct' + i"
1630
- data-testid="category-tick"
1631
- :x="tick.pos"
1632
- :y="padding.top + innerH + 16"
1633
- :text-anchor="tick.anchor"
1634
- :font-size="tickLabelResolved.fontSize"
1635
- :fill="tickLabelResolved.fill"
1636
- :font-weight="tickLabelResolved.fontWeight"
1637
- :fill-opacity="tickLabelResolved.fillOpacity"
1683
+ v-if="xLabel"
1684
+ :x="padding.left + innerW / 2"
1685
+ :y="height - 4"
1686
+ text-anchor="middle"
1687
+ :font-size="axisLabelResolved.fontSize"
1688
+ :fill="axisLabelResolved.fill"
1689
+ :font-weight="axisLabelResolved.fontWeight"
1638
1690
  >
1639
- {{ tick.label }}
1691
+ {{ xLabel }}
1640
1692
  </text>
1641
- </template>
1642
- <template v-else>
1693
+ <!-- bars -->
1694
+ <rect
1695
+ v-for="(bar, i) in bars"
1696
+ :key="'bar' + i"
1697
+ data-testid="bar"
1698
+ :data-category="bar.categoryIndex"
1699
+ :data-series="bar.seriesIndex"
1700
+ :x="bar.x"
1701
+ :y="bar.y"
1702
+ :width="bar.w"
1703
+ :height="bar.h"
1704
+ :fill="bar.color"
1705
+ :fill-opacity="bar.opacity"
1706
+ :style="bar.blendMode ? { mixBlendMode: bar.blendMode } : undefined"
1707
+ />
1708
+ <!-- value-on-bar labels -->
1643
1709
  <text
1644
- v-for="(tick, i) in categoryTickItems"
1645
- :key="'ct' + i"
1646
- data-testid="category-tick"
1647
- :x="categoryLabelLayout.x"
1648
- :y="tick.pos"
1649
- :text-anchor="categoryLabelLayout.anchor"
1710
+ v-for="item in barLabelItems"
1711
+ :key="'blbl' + item.key"
1712
+ data-testid="bar-label"
1713
+ :x="item.x"
1714
+ :y="item.y"
1715
+ :text-anchor="item.anchor"
1650
1716
  dominant-baseline="middle"
1651
- :font-size="tickLabelResolved.fontSize"
1652
- :fill="tickLabelResolved.fill"
1653
- :font-weight="tickLabelResolved.fontWeight"
1654
- :fill-opacity="tickLabelResolved.fillOpacity"
1717
+ :font-size="item.fontSize"
1718
+ :font-weight="item.fontWeight"
1719
+ :fill="item.fill"
1720
+ pointer-events="none"
1655
1721
  >
1656
- {{ tick.label }}
1722
+ {{ item.text }}
1657
1723
  </text>
1658
- </template>
1659
- <!-- x axis label -->
1660
- <text
1661
- v-if="xLabel"
1662
- :x="padding.left + innerW / 2"
1663
- :y="height - 4"
1664
- text-anchor="middle"
1665
- :font-size="axisLabelResolved.fontSize"
1666
- :fill="axisLabelResolved.fill"
1667
- :font-weight="axisLabelResolved.fontWeight"
1668
- >
1669
- {{ xLabel }}
1670
- </text>
1671
- <!-- bars -->
1672
- <rect
1673
- v-for="(bar, i) in bars"
1674
- :key="'bar' + i"
1675
- data-testid="bar"
1676
- :data-category="bar.categoryIndex"
1677
- :data-series="bar.seriesIndex"
1678
- :x="bar.x"
1679
- :y="bar.y"
1680
- :width="bar.w"
1681
- :height="bar.h"
1682
- :fill="bar.color"
1683
- :fill-opacity="bar.opacity"
1684
- :style="bar.blendMode ? { mixBlendMode: bar.blendMode } : undefined"
1685
- />
1686
- <!-- value-on-bar labels -->
1687
- <text
1688
- v-for="item in barLabelItems"
1689
- :key="'blbl' + item.key"
1690
- data-testid="bar-label"
1691
- :x="item.x"
1692
- :y="item.y"
1693
- :text-anchor="item.anchor"
1694
- dominant-baseline="middle"
1695
- :font-size="item.fontSize"
1696
- :font-weight="item.fontWeight"
1697
- :fill="item.fill"
1698
- pointer-events="none"
1699
- >
1700
- {{ item.text }}
1701
- </text>
1702
- <!-- summary lines (drawn above bars, below annotations) -->
1703
- <template v-for="(line, i) in summaryLinesResolved" :key="'sl' + i">
1704
- <path
1705
- v-if="line.pathD"
1706
- data-testid="summary-line"
1707
- :d="line.pathD"
1708
- fill="none"
1709
- :stroke="line.color"
1710
- :stroke-width="line.strokeWidth"
1711
- :stroke-opacity="line.opacity"
1712
- :stroke-dasharray="line.dashed ? '6 3' : undefined"
1713
- stroke-linecap="round"
1714
- stroke-linejoin="round"
1715
- :style="line.blendMode ? { mixBlendMode: line.blendMode } : undefined"
1716
- />
1717
- <template v-if="line.dots">
1718
- <circle
1719
- v-for="(pt, j) in line.points"
1720
- :key="'sld' + i + '-' + j"
1721
- :cx="pt.x"
1722
- :cy="pt.y"
1723
- :r="line.dotRadius"
1724
- :fill="line.color"
1725
- :fill-opacity="line.opacity"
1724
+ <!-- summary lines (drawn above bars, below annotations) -->
1725
+ <template v-for="(line, i) in summaryLinesResolved" :key="'sl' + i">
1726
+ <path
1727
+ v-if="line.pathD"
1728
+ data-testid="summary-line"
1729
+ :d="line.pathD"
1730
+ fill="none"
1731
+ :stroke="line.color"
1732
+ :stroke-width="line.strokeWidth"
1733
+ :stroke-opacity="line.opacity"
1734
+ :stroke-dasharray="line.dashed ? '6 3' : undefined"
1735
+ stroke-linecap="round"
1736
+ stroke-linejoin="round"
1726
1737
  :style="
1727
1738
  line.blendMode ? { mixBlendMode: line.blendMode } : undefined
1728
1739
  "
1729
1740
  />
1730
- </template>
1731
- </template>
1732
- <!-- Tooltip: interaction overlay -->
1733
- <rect
1734
- v-if="hasTooltipSlot"
1735
- :x="padding.left"
1736
- :y="padding.top"
1737
- :width="innerW"
1738
- :height="innerH"
1739
- fill="transparent"
1740
- style="cursor: crosshair; touch-action: none"
1741
- v-on="tooltipHandlers"
1742
- />
1743
- <!-- annotations (top layer) -->
1744
- <ChartAnnotations
1745
- v-if="annotations && annotations.length > 0"
1746
- :annotations="annotations"
1747
- :project="projectAnnotation"
1748
- :bounds="bounds"
1749
- />
1750
- </svg>
1751
- <!-- Tooltip floating content -->
1752
- <div
1753
- v-if="hasTooltipSlot && hoverIndex !== null && hoverSlotProps"
1754
- ref="tooltipRef"
1755
- class="chart-tooltip-content"
1756
- :style="{
1757
- position: 'absolute',
1758
- top: '0',
1759
- left: '0',
1760
- willChange: 'transform',
1761
- transform: tooltipPos
1762
- ? `translate3d(${tooltipPos.left}px, ${tooltipPos.top}px, 0) translateY(-50%)`
1763
- : 'translateY(-50%)',
1764
- visibility: tooltipPos ? 'visible' : 'hidden',
1765
- }"
1766
- >
1767
- <slot name="tooltip" v-bind="hoverSlotProps">
1768
- <div class="bar-chart-tooltip">
1769
- <div v-if="hoveredCategoryLabel" class="bar-chart-tooltip-label">
1770
- {{ hoveredCategoryLabel }}
1771
- </div>
1772
- <div
1773
- v-for="v in hoverSlotProps.values"
1774
- :key="v.seriesIndex"
1775
- class="bar-chart-tooltip-row"
1776
- >
1777
- <span
1778
- class="bar-chart-tooltip-swatch"
1779
- :style="{ background: v.color }"
1741
+ <template v-if="line.dots">
1742
+ <circle
1743
+ v-for="(pt, j) in line.points"
1744
+ :key="'sld' + i + '-' + j"
1745
+ :cx="pt.x"
1746
+ :cy="pt.y"
1747
+ :r="line.dotRadius"
1748
+ :fill="line.color"
1749
+ :fill-opacity="line.opacity"
1750
+ :style="
1751
+ line.blendMode ? { mixBlendMode: line.blendMode } : undefined
1752
+ "
1780
1753
  />
1781
- {{ isFinite(v.value) ? formatTooltipValue(v.value) : "—" }}
1754
+ </template>
1755
+ </template>
1756
+ <!-- Tooltip: interaction overlay -->
1757
+ <rect
1758
+ v-if="hasTooltipSlot"
1759
+ :x="padding.left"
1760
+ :y="padding.top"
1761
+ :width="innerW"
1762
+ :height="innerH"
1763
+ fill="transparent"
1764
+ :style="`cursor: crosshair; touch-action: ${isVertical ? 'pan-y' : 'pan-x'}`"
1765
+ v-on="tooltipHandlers"
1766
+ />
1767
+ <!-- annotations (top layer) -->
1768
+ <ChartAnnotations
1769
+ v-if="annotations && annotations.length > 0"
1770
+ :annotations="annotations"
1771
+ :project="projectAnnotation"
1772
+ :bounds="bounds"
1773
+ />
1774
+ </svg>
1775
+ <!-- Tooltip floating content -->
1776
+ <div
1777
+ v-if="hasTooltipSlot && hoverIndex !== null && hoverSlotProps"
1778
+ ref="tooltipRef"
1779
+ class="chart-tooltip-content"
1780
+ :style="{
1781
+ position: 'absolute',
1782
+ top: '0',
1783
+ left: '0',
1784
+ willChange: 'transform',
1785
+ transform: tooltipPos
1786
+ ? `translate3d(${tooltipPos.left}px, ${tooltipPos.top}px, 0) translateY(-50%)`
1787
+ : 'translateY(-50%)',
1788
+ visibility: tooltipPos ? 'visible' : 'hidden',
1789
+ }"
1790
+ >
1791
+ <slot name="tooltip" v-bind="hoverSlotProps">
1792
+ <div class="bar-chart-tooltip">
1793
+ <div v-if="hoveredCategoryLabel" class="bar-chart-tooltip-label">
1794
+ {{ hoveredCategoryLabel }}
1795
+ </div>
1796
+ <div
1797
+ v-for="v in hoverSlotProps.values"
1798
+ :key="v.seriesIndex"
1799
+ class="bar-chart-tooltip-row"
1800
+ >
1801
+ <span
1802
+ class="bar-chart-tooltip-swatch"
1803
+ :style="{ background: v.color }"
1804
+ />
1805
+ {{ isFinite(v.value) ? formatTooltipValue(v.value) : "—" }}
1806
+ </div>
1782
1807
  </div>
1783
- </div>
1784
- </slot>
1808
+ </slot>
1809
+ </div>
1810
+ <a
1811
+ v-if="downloadLinkText"
1812
+ class="bar-chart-download-link"
1813
+ :href="csvHref!"
1814
+ :download="`${menuFilename()}.csv`"
1815
+ >
1816
+ {{ downloadLinkText }}
1817
+ </a>
1818
+ <button
1819
+ v-if="downloadButtonText"
1820
+ type="button"
1821
+ class="bar-chart-download-button"
1822
+ @click="triggerCsvDownload"
1823
+ >
1824
+ {{ downloadButtonText }}
1825
+ </button>
1785
1826
  </div>
1786
- <a
1787
- v-if="downloadLinkText"
1788
- class="bar-chart-download-link"
1789
- :href="csvHref!"
1790
- :download="`${menuFilename()}.csv`"
1791
- >
1792
- {{ downloadLinkText }}
1793
- </a>
1794
- <button
1795
- v-if="downloadButtonText"
1796
- type="button"
1797
- class="bar-chart-download-button"
1798
- @click="triggerCsvDownload"
1799
- >
1800
- {{ downloadButtonText }}
1801
- </button>
1802
- </div>
1827
+ </Teleport>
1803
1828
  </template>
1804
1829
 
1805
1830
  <style scoped>