basalt-ui 1.14.0 → 1.15.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.
Files changed (162) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +8 -0
  3. package/README.md +38 -2
  4. package/agent/rules/basalt-charts.md +260 -108
  5. package/agent/rules/basalt-mantine.md +4 -2
  6. package/agent/skills/basalt-charts/SKILL.md +114 -64
  7. package/agent/skills/basalt-design/SKILL.md +4 -3
  8. package/configs/oxlint-plugin.js +152 -0
  9. package/configs/oxlint-plugin.test.ts +107 -0
  10. package/configs/oxlint.json +2 -0
  11. package/dist/charts/cursor/resolve.d.ts +33 -0
  12. package/dist/charts/cursor/resolve.d.ts.map +1 -0
  13. package/dist/charts/cursor/resolve.js +61 -0
  14. package/dist/charts/cursor/resolve.test.d.ts +2 -0
  15. package/dist/charts/cursor/resolve.test.d.ts.map +1 -0
  16. package/dist/charts/cursor/scope.d.ts +17 -0
  17. package/dist/charts/cursor/scope.d.ts.map +1 -0
  18. package/dist/charts/cursor/scope.js +20 -0
  19. package/dist/charts/cursor/store.d.ts +31 -0
  20. package/dist/charts/cursor/store.d.ts.map +1 -0
  21. package/dist/charts/cursor/store.js +25 -0
  22. package/dist/charts/cursor/store.test.d.ts +2 -0
  23. package/dist/charts/cursor/store.test.d.ts.map +1 -0
  24. package/dist/charts/hooks/useChartCursor.d.ts +39 -0
  25. package/dist/charts/hooks/useChartCursor.d.ts.map +1 -0
  26. package/dist/charts/hooks/useChartCursor.js +104 -0
  27. package/dist/charts/hooks/useChartSize.d.ts +8 -4
  28. package/dist/charts/hooks/useChartSize.d.ts.map +1 -1
  29. package/dist/charts/index.d.ts +16 -13
  30. package/dist/charts/index.d.ts.map +1 -1
  31. package/dist/charts/index.js +38 -19
  32. package/dist/charts/kinds/Bars.d.ts +25 -47
  33. package/dist/charts/kinds/Bars.d.ts.map +1 -1
  34. package/dist/charts/kinds/Bars.js +230 -390
  35. package/dist/charts/kinds/Donut.d.ts +5 -4
  36. package/dist/charts/kinds/Donut.d.ts.map +1 -1
  37. package/dist/charts/kinds/Donut.js +17 -14
  38. package/dist/charts/kinds/DualPanel.d.ts.map +1 -1
  39. package/dist/charts/kinds/DualPanel.js +136 -90
  40. package/dist/charts/kinds/Heatmap.d.ts +15 -11
  41. package/dist/charts/kinds/Heatmap.d.ts.map +1 -1
  42. package/dist/charts/kinds/Heatmap.js +38 -22
  43. package/dist/charts/kinds/MultiLine.d.ts +27 -35
  44. package/dist/charts/kinds/MultiLine.d.ts.map +1 -1
  45. package/dist/charts/kinds/MultiLine.js +114 -237
  46. package/dist/charts/kinds/StackedArea.d.ts +21 -10
  47. package/dist/charts/kinds/StackedArea.d.ts.map +1 -1
  48. package/dist/charts/kinds/StackedArea.js +74 -132
  49. package/dist/charts/kinds/ZonedLine.d.ts +24 -40
  50. package/dist/charts/kinds/ZonedLine.d.ts.map +1 -1
  51. package/dist/charts/kinds/ZonedLine.js +103 -184
  52. package/dist/charts/layout/auto-margin.d.ts +60 -0
  53. package/dist/charts/layout/auto-margin.d.ts.map +1 -0
  54. package/dist/charts/layout/auto-margin.js +37 -0
  55. package/dist/charts/layout/auto-margin.test.d.ts +2 -0
  56. package/dist/charts/layout/auto-margin.test.d.ts.map +1 -0
  57. package/dist/charts/primitives/CartesianChart.d.ts +143 -0
  58. package/dist/charts/primitives/CartesianChart.d.ts.map +1 -0
  59. package/dist/charts/primitives/CartesianChart.js +349 -0
  60. package/dist/charts/primitives/CartesianChart.test.d.ts +2 -0
  61. package/dist/charts/primitives/CartesianChart.test.d.ts.map +1 -0
  62. package/dist/charts/primitives/ChartFrame.d.ts +16 -5
  63. package/dist/charts/primitives/ChartFrame.d.ts.map +1 -1
  64. package/dist/charts/primitives/ChartFrame.js +17 -2
  65. package/dist/charts/primitives/ChartLegend.d.ts +12 -1
  66. package/dist/charts/primitives/ChartLegend.d.ts.map +1 -1
  67. package/dist/charts/primitives/ChartLegend.js +21 -3
  68. package/dist/charts/primitives/ChartLegend.test.d.ts +2 -0
  69. package/dist/charts/primitives/ChartLegend.test.d.ts.map +1 -0
  70. package/dist/charts/primitives/ChartTooltip.d.ts +16 -16
  71. package/dist/charts/primitives/ChartTooltip.d.ts.map +1 -1
  72. package/dist/charts/primitives/ChartTooltip.js +45 -19
  73. package/dist/charts/primitives/HoverOverlay.d.ts +19 -4
  74. package/dist/charts/primitives/HoverOverlay.d.ts.map +1 -1
  75. package/dist/charts/primitives/HoverOverlay.js +22 -1
  76. package/dist/charts/primitives/XZoneRects.d.ts +40 -0
  77. package/dist/charts/primitives/XZoneRects.d.ts.map +1 -0
  78. package/dist/charts/primitives/XZoneRects.js +28 -0
  79. package/dist/charts/primitives/XZoneRects.test.d.ts +2 -0
  80. package/dist/charts/primitives/XZoneRects.test.d.ts.map +1 -0
  81. package/dist/charts/series.d.ts +16 -1
  82. package/dist/charts/series.d.ts.map +1 -1
  83. package/dist/charts/series.js +3 -1
  84. package/dist/charts/series.test.d.ts +2 -0
  85. package/dist/charts/series.test.d.ts.map +1 -0
  86. package/dist/charts/utils/measure-text.d.ts +25 -0
  87. package/dist/charts/utils/measure-text.d.ts.map +1 -0
  88. package/dist/charts/utils/measure-text.js +64 -0
  89. package/dist/charts/utils/measure-text.test.d.ts +2 -0
  90. package/dist/charts/utils/measure-text.test.d.ts.map +1 -0
  91. package/dist/content/fence-block.d.ts.map +1 -1
  92. package/dist/surfaces.d.ts +1 -1
  93. package/dist/surfaces.d.ts.map +1 -1
  94. package/dist/surfaces.js +1 -1
  95. package/dist/tokens/chart-margin.test.d.ts +2 -0
  96. package/dist/tokens/chart-margin.test.d.ts.map +1 -0
  97. package/dist/tokens/index.d.ts +29 -0
  98. package/dist/tokens/index.d.ts.map +1 -1
  99. package/dist/tokens/index.js +10 -0
  100. package/dist/tokens/palette.d.ts +4 -0
  101. package/dist/tokens/palette.d.ts.map +1 -1
  102. package/dist/tokens/palette.js +3 -0
  103. package/llms.txt +3 -3
  104. package/package.json +1 -1
  105. package/src/charts/cursor/resolve.test.ts +61 -0
  106. package/src/charts/cursor/resolve.ts +102 -0
  107. package/src/charts/cursor/scope.tsx +32 -0
  108. package/src/charts/cursor/store.test.ts +46 -0
  109. package/src/charts/cursor/store.ts +53 -0
  110. package/src/charts/hooks/useChartCursor.ts +163 -0
  111. package/src/charts/hooks/useChartSize.ts +8 -4
  112. package/src/charts/index.ts +34 -14
  113. package/src/charts/kinds/Bars.tsx +327 -460
  114. package/src/charts/kinds/Donut.tsx +31 -23
  115. package/src/charts/kinds/DualPanel.tsx +228 -134
  116. package/src/charts/kinds/Heatmap.tsx +72 -47
  117. package/src/charts/kinds/MultiLine.tsx +158 -294
  118. package/src/charts/kinds/StackedArea.tsx +116 -175
  119. package/src/charts/kinds/ZonedLine.tsx +148 -255
  120. package/src/charts/layout/auto-margin.test.ts +42 -0
  121. package/src/charts/layout/auto-margin.ts +110 -0
  122. package/src/charts/primitives/CartesianChart.test.ts +99 -0
  123. package/src/charts/primitives/CartesianChart.tsx +566 -0
  124. package/src/charts/primitives/ChartFrame.test.tsx +33 -0
  125. package/src/charts/primitives/ChartFrame.tsx +40 -4
  126. package/src/charts/primitives/ChartLegend.test.tsx +48 -0
  127. package/src/charts/primitives/ChartLegend.tsx +45 -2
  128. package/src/charts/primitives/ChartTooltip.tsx +67 -37
  129. package/src/charts/primitives/HoverOverlay.tsx +39 -3
  130. package/src/charts/primitives/XZoneRects.test.tsx +63 -0
  131. package/src/charts/primitives/XZoneRects.tsx +67 -0
  132. package/src/charts/series.test.ts +61 -0
  133. package/src/charts/series.ts +18 -1
  134. package/src/charts/utils/measure-text.test.ts +36 -0
  135. package/src/charts/utils/measure-text.ts +109 -0
  136. package/src/content/fence-block.tsx +2 -3
  137. package/src/surfaces.ts +2 -1
  138. package/src/theme/spacing.test.ts +2 -0
  139. package/src/tokens/chart-margin.test.ts +39 -0
  140. package/src/tokens/density.test.ts +1 -0
  141. package/src/tokens/index.ts +36 -0
  142. package/src/tokens/palette.ts +3 -0
  143. package/dist/charts/hooks/useChartTooltip.d.ts +0 -13
  144. package/dist/charts/hooks/useChartTooltip.d.ts.map +0 -1
  145. package/dist/charts/hooks/useChartTooltip.js +0 -26
  146. package/dist/charts/hooks/useHoverSync.d.ts +0 -46
  147. package/dist/charts/hooks/useHoverSync.d.ts.map +0 -1
  148. package/dist/charts/hooks/useHoverSync.js +0 -75
  149. package/dist/charts/hover-context.d.ts +0 -21
  150. package/dist/charts/hover-context.d.ts.map +0 -1
  151. package/dist/charts/hover-context.js +0 -12
  152. package/dist/charts/hover-sync.d.ts +0 -14
  153. package/dist/charts/hover-sync.d.ts.map +0 -1
  154. package/dist/charts/hover-sync.js +0 -17
  155. package/dist/charts/primitives/ResponsiveChart.d.ts +0 -45
  156. package/dist/charts/primitives/ResponsiveChart.d.ts.map +0 -1
  157. package/dist/charts/primitives/ResponsiveChart.js +0 -24
  158. package/src/charts/hooks/useChartTooltip.ts +0 -31
  159. package/src/charts/hooks/useHoverSync.ts +0 -152
  160. package/src/charts/hover-context.ts +0 -28
  161. package/src/charts/hover-sync.tsx +0 -37
  162. package/src/charts/primitives/ResponsiveChart.tsx +0 -64
@@ -52,10 +52,11 @@ export type DonutProps<K extends string = SeriesKey> = {
52
52
  /**
53
53
  * Radial slice-share chart with a punched-out center label. Composes `ChartFrame` for a
54
54
  * categorical legend derived from the slices (one `SeriesStyle` per slice, `mark: 'bar'`) so the
55
- * legend can never drift from what's plotted. No crosshair — meaningless for a radial layout.
56
- * Hover stays local to the pie (dimming siblings on hover) rather than joining the shared
57
- * `HoverContext`: a date-keyed cursor has no counterpart on a donut, and cross-kind category sync
58
- * (donut ↔ bar, via the generalized key) is a distinct, deliberately deferred feature.
55
+ * legend can never drift from what's plotted — legend-hidden slices drop out of the ring, the
56
+ * center total, and the tooltip's "Share" row together (`docs/CHARTS-SPEC.md` §5). No crosshair —
57
+ * meaningless for a radial layout. Hover stays local to the pie (dimming siblings on hover) rather
58
+ * than joining the shared cursor: a date-keyed cursor has no counterpart on a donut, and cross-kind
59
+ * category sync (donut ↔ bar, via a generalized key) is a distinct, deliberately deferred feature.
59
60
  */
60
61
  declare function DonutInner<K extends string = SeriesKey>(props: DonutProps<K>): import("react").JSX.Element;
61
62
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"Donut.d.ts","sourceRoot":"","sources":["../../../src/charts/kinds/Donut.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAMtC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAE/C;;;;GAIG;AACH,MAAM,MAAM,UAAU,CAAC,CAAC,SAAS,MAAM,GAAG,SAAS,IAAI;IAAE,GAAG,EAAE,CAAC,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA;AAEhF,MAAM,MAAM,UAAU,CAAC,CAAC,SAAS,MAAM,GAAG,SAAS,IAAI;IACrD,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,CAAA;IACrB,mFAAmF;IACnF,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,WAAW,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,MAAM,CAAA;IAC/B,WAAW,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAA;IAClC,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAA;IACrC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,aAAa,CAAC,EAAE,SAAS,CAAA;IACzB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,+FAA+F;IAC/F,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,4FAA4F;IAC5F,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB,CAAA;AAED;;;;;;;GAOG;AACH,iBAAS,UAAU,CAAC,CAAC,SAAS,MAAM,GAAG,SAAS,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,+BAoBrE;AAmJD;;;GAGG;AACH,eAAO,MAAM,KAAK,EAAuB,OAAO,UAAU,CAAA"}
1
+ {"version":3,"file":"Donut.d.ts","sourceRoot":"","sources":["../../../src/charts/kinds/Donut.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAgB,SAAS,EAAE,MAAM,OAAO,CAAA;AAKpD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAE/C;;;;GAIG;AACH,MAAM,MAAM,UAAU,CAAC,CAAC,SAAS,MAAM,GAAG,SAAS,IAAI;IAAE,GAAG,EAAE,CAAC,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA;AAEhF,MAAM,MAAM,UAAU,CAAC,CAAC,SAAS,MAAM,GAAG,SAAS,IAAI;IACrD,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,CAAA;IACrB,mFAAmF;IACnF,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,WAAW,EAAE,CAAC,GAAG,EAAE,CAAC,KAAK,MAAM,CAAA;IAC/B,WAAW,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAA;IAClC,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAA;IACrC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB;;;;;;;;;;;;;;;;;;;;;;OAsBG;IACH,aAAa,CAAC,EAAE,SAAS,CAAA;IACzB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,+FAA+F;IAC/F,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,4FAA4F;IAC5F,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB,CAAA;AAED;;;;;;;;GAQG;AACH,iBAAS,UAAU,CAAC,CAAC,SAAS,MAAM,GAAG,SAAS,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,+BAoBrE;AA2JD;;;GAGG;AACH,eAAO,MAAM,KAAK,EAAuB,OAAO,UAAU,CAAA"}
@@ -1,10 +1,9 @@
1
1
  import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import { Group } from "@visx/group";
3
3
  import { Pie } from "@visx/shape";
4
- import { memo, useMemo } from "react";
5
- import { ChartTooltip, TooltipBody, TooltipRow, useTooltipStyles } from "../primitives/ChartTooltip.js";
4
+ import { memo, useMemo, useState } from "react";
5
+ import { ChartTooltipFloat, TooltipBody, TooltipRow } from "../primitives/ChartTooltip.js";
6
6
  import { ChartFrame } from "../primitives/ChartFrame.js";
7
- import { useChartTooltip } from "../hooks/useChartTooltip.js";
8
7
  import { VX } from "../../tokens/index.js";
9
8
  function DonutInner(props) {
10
9
  const { data, height, colorForKey, seriesLabel = (k) => k, ariaLabel, isPending } = props;
@@ -38,21 +37,25 @@ function DonutPlot(props) {
38
37
  innerRatio = 0.6,
39
38
  padAngle = 0.01
40
39
  } = props;
41
- const { width, height } = plot;
42
- const tooltipStyles = useTooltipStyles();
43
- const { tip, show, hide, tooltipRef } = useChartTooltip();
44
- const hoveredKey = tip?.data.key ?? null;
40
+ const { width, height, hidden } = plot;
41
+ const [tip, setTip] = useState(null);
42
+ const hoveredKey = tip?.key ?? null;
43
+ const visibleData = useMemo(() => data.filter((d) => !hidden.has(d.key)), [data, hidden]);
45
44
  const radius = Math.min(width, height) / 2 - 4;
46
45
  const innerRadius = radius * innerRatio;
47
46
  const centerX = width / 2;
48
47
  const centerY = height / 2;
49
- const total = useMemo(() => data.reduce((sum, d) => sum + d.value, 0), [data]);
48
+ const total = useMemo(() => visibleData.reduce((sum, d) => sum + d.value, 0), [visibleData]);
49
+ const show = (d, event) => {
50
+ setTip({ key: d.key, value: d.value, anchor: { x: event.clientX, y: event.clientY } });
51
+ };
52
+ const hide = () => setTip(null);
50
53
  return /* @__PURE__ */ jsxs("div", { style: { position: "relative" }, children: [
51
54
  /* @__PURE__ */ jsx("svg", { width, height, children: /* @__PURE__ */ jsxs(Group, { left: centerX, top: centerY, children: [
52
55
  /* @__PURE__ */ jsx(
53
56
  Pie,
54
57
  {
55
- data,
58
+ data: visibleData,
56
59
  pieValue: (d) => d.value,
57
60
  pieSortValues: () => 0,
58
61
  outerRadius: radius,
@@ -135,13 +138,13 @@ function DonutPlot(props) {
135
138
  children: centerContent
136
139
  }
137
140
  ),
138
- /* @__PURE__ */ jsx(ChartTooltip, { tip, tooltipRef, styles: tooltipStyles, children: tip && /* @__PURE__ */ jsxs(TooltipBody, { children: [
141
+ tip !== null && /* @__PURE__ */ jsx(ChartTooltipFloat, { anchor: tip.anchor, children: /* @__PURE__ */ jsxs(TooltipBody, { children: [
139
142
  /* @__PURE__ */ jsx(
140
143
  TooltipRow,
141
144
  {
142
- color: colorForKey(tip.data.key),
143
- label: seriesLabel(tip.data.key),
144
- value: formatValue(tip.data.value),
145
+ color: colorForKey(tip.key),
146
+ label: seriesLabel(tip.key),
147
+ value: formatValue(tip.value),
145
148
  shape: "bar"
146
149
  }
147
150
  ),
@@ -150,7 +153,7 @@ function DonutPlot(props) {
150
153
  {
151
154
  color: VX.grid,
152
155
  label: "Share",
153
- value: `${total > 0 ? Math.round(tip.data.value / total * 100) : 0}%`,
156
+ value: `${total > 0 ? Math.round(tip.value / total * 100) : 0}%`,
154
157
  shape: "bar"
155
158
  }
156
159
  )
@@ -1 +1 @@
1
- {"version":3,"file":"DualPanel.d.ts","sourceRoot":"","sources":["../../../src/charts/kinds/DualPanel.tsx"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AAGvD,OAAO,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAe,MAAM,WAAW,CAAA;AAI5E,MAAM,MAAM,cAAc,CAAC,CAAC,IAAI;IAC9B,IAAI,EAAE,CAAC,EAAE,CAAA;IACT,mFAAmF;IACnF,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,CAAA;IACtB;8DAC0D;IAC1D,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,CAAA;IACxB,+EAA+E;IAC/E,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAA;IACtC,2EAA2E;IAC3E,WAAW,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;IACxD,wGAAwG;IACxG,WAAW,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,EAAE,CAAA;IAClE,oDAAoD;IACpD,QAAQ,CAAC,EAAE,QAAQ,EAAE,CAAA;IACrB,8DAA8D;IAC9D,MAAM,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,GAAG,IAAI,CAAA;IAC/B,QAAQ,EAAE,MAAM,CAAA;IAChB,gBAAgB,EAAE,MAAM,CAAA;IACxB,gBAAgB,EAAE,MAAM,CAAA;IACxB,6DAA6D;IAC7D,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,SAAS,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAA;IAChC,YAAY,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAA;IACnC,kEAAkE;IAClE,YAAY,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;IAC/D;4CACwC;IACxC,MAAM,CAAC,EAAE,iBAAiB,GAAG,KAAK,CAAA;IAClC,+FAA+F;IAC/F,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,4FAA4F;IAC5F,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB,CAAA;AAID;;;;;;;;;;;GAWG;AACH,iBAAS,cAAc,CAAC,CAAC,EAAE,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC,+BAmClD;AA+TD;;;GAGG;AACH,eAAO,MAAM,SAAS,EAA2B,OAAO,cAAc,CAAA"}
1
+ {"version":3,"file":"DualPanel.d.ts","sourceRoot":"","sources":["../../../src/charts/kinds/DualPanel.tsx"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,yBAAyB,CAAA;AAIvD,OAAO,KAAK,EAAE,iBAAiB,EAAE,WAAW,EAAe,MAAM,WAAW,CAAA;AAK5E,MAAM,MAAM,cAAc,CAAC,CAAC,IAAI;IAC9B,IAAI,EAAE,CAAC,EAAE,CAAA;IACT,mFAAmF;IACnF,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,CAAA;IACtB;8DAC0D;IAC1D,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,CAAA;IACxB,+EAA+E;IAC/E,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAAA;IACtC,2EAA2E;IAC3E,WAAW,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAA;IACxD,wGAAwG;IACxG,WAAW,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,OAAO,CAAA;KAAE,EAAE,CAAA;IAClE,oDAAoD;IACpD,QAAQ,CAAC,EAAE,QAAQ,EAAE,CAAA;IACrB,8DAA8D;IAC9D,MAAM,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,GAAG,IAAI,CAAA;IAC/B,QAAQ,EAAE,MAAM,CAAA;IAChB,gBAAgB,EAAE,MAAM,CAAA;IACxB,gBAAgB,EAAE,MAAM,CAAA;IACxB,6DAA6D;IAC7D,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,SAAS,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAA;IAChC,YAAY,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAA;IACnC,kEAAkE;IAClE,YAAY,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;IAC/D;4CACwC;IACxC,MAAM,CAAC,EAAE,iBAAiB,GAAG,KAAK,CAAA;IAClC,+FAA+F;IAC/F,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,4FAA4F;IAC5F,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB,CAAA;AAID;;;;;;;;;;;GAWG;AACH,iBAAS,cAAc,CAAC,CAAC,EAAE,KAAK,EAAE,cAAc,CAAC,CAAC,CAAC,+BAmClD;AA4ZD;;;GAGG;AACH,eAAO,MAAM,SAAS,EAA2B,OAAO,cAAc,CAAA"}
@@ -8,19 +8,20 @@ import { Threshold } from "@visx/threshold";
8
8
  import { memo, useMemo } from "react";
9
9
  import { AxisBottomDate, AxisLeftNumeric } from "../primitives/Axes.js";
10
10
  import {
11
- ChartTooltip,
11
+ ChartTooltipFloat,
12
12
  TooltipBody,
13
13
  TooltipHeader,
14
- TooltipRow,
15
- useTooltipStyles
14
+ TooltipRow
16
15
  } from "../primitives/ChartTooltip.js";
17
16
  import { ChartFrame, resolveLegend } from "../primitives/ChartFrame.js";
18
17
  import { Crosshair, SeriesDot } from "../primitives/Crosshair.js";
19
18
  import { HoverOverlay } from "../primitives/HoverOverlay.js";
20
19
  import { ZoneRects } from "../primitives/ZoneRects.js";
21
- import { useHoverSync } from "../hooks/useHoverSync.js";
20
+ import { useChartCursor } from "../hooks/useChartCursor.js";
21
+ import { autoMargin, probeAxisLabels } from "../layout/auto-margin.js";
22
22
  import { deriveTooltipRows, LINE_OVERLAY_STROKE_WIDTH } from "../series.js";
23
23
  import { VX } from "../../tokens/index.js";
24
+ import { fmtAxisDate } from "../utils/format.js";
24
25
  import { smartTicks } from "../utils/ticks.js";
25
26
  const PANE_GAP = 12;
26
27
  function DualPanelInner(props) {
@@ -69,24 +70,15 @@ function DualPanelPlot(props) {
69
70
  tooltipLabel,
70
71
  plot
71
72
  } = props;
72
- const MARGIN = VX.margin;
73
- const xMax = plot.width - MARGIN.left - MARGIN.right;
74
- const plotH = plot.height - MARGIN.top - MARGIN.bottom - PANE_GAP;
75
- const topH = Math.max(Math.round(plotH * topFraction), 1);
76
- const bottomH = Math.max(plotH - topH, 1);
77
- const bottomTop = topH + PANE_GAP;
78
- const innerH = topH + PANE_GAP + bottomH;
79
- const xScale = useMemo(
80
- () => scalePoint({ domain: data.map(getX), range: [0, xMax], padding: 0.3 }),
81
- [data, xMax, getX]
82
- );
83
- const topYScale = useMemo(() => {
84
- if (topYDomain !== "auto") {
85
- return scaleLinear({ domain: topYDomain, range: [topH, 0] });
86
- }
73
+ const { hidden } = plot;
74
+ const visibleSeries = useMemo(() => series.filter((s) => !hidden.has(s.key)), [series, hidden]);
75
+ const barVisible = !hidden.has("__divergence");
76
+ const useNiceTop = topYDomain === "auto";
77
+ const topDomain = useMemo(() => {
78
+ if (topYDomain !== "auto") return topYDomain;
87
79
  let lo = Infinity;
88
80
  let hi = -Infinity;
89
- for (const s of series) {
81
+ for (const s of visibleSeries) {
90
82
  for (const d of data) {
91
83
  const v = s.getValue(d);
92
84
  if (v === null || v === void 0 || Number.isNaN(v)) continue;
@@ -99,43 +91,101 @@ function DualPanelPlot(props) {
99
91
  hi = 1;
100
92
  }
101
93
  const span = hi - lo || Math.abs(hi) || 1;
102
- return scaleLinear({
103
- domain: [lo - span * 0.08, hi + span * 0.08],
104
- range: [topH, 0],
105
- nice: true
106
- });
107
- }, [data, series, topYDomain, topH]);
108
- const bottomYScale = useMemo(() => {
109
- let maxAbs = 0;
94
+ return [lo - span * 0.08, hi + span * 0.08];
95
+ }, [data, visibleSeries, topYDomain]);
96
+ const topLabels = useMemo(
97
+ () => probeAxisLabels({ domain: topDomain, ticks: 4, format: formatTop, nice: useNiceTop }).labels,
98
+ [topDomain, useNiceTop, formatTop]
99
+ );
100
+ const maxAbs = useMemo(() => {
101
+ let m = 0;
110
102
  for (const d of data) {
111
103
  const v = getBar(d);
112
104
  if (v === null || v === void 0 || Number.isNaN(v)) continue;
113
105
  const a = Math.abs(v);
114
- if (a > maxAbs) maxAbs = a;
106
+ if (a > m) m = a;
115
107
  }
116
- if (maxAbs === 0) maxAbs = 1;
117
- return scaleLinear({
108
+ return m === 0 ? 1 : m;
109
+ }, [data, getBar]);
110
+ const bottomLabels = useMemo(
111
+ () => probeAxisLabels({
118
112
  domain: [-maxAbs, maxAbs],
119
- range: [bottomH, 0],
113
+ ticks: 3,
114
+ format: formatBottom,
120
115
  nice: true
121
- });
122
- }, [data, getBar, bottomH]);
123
- const tooltipStyles = useTooltipStyles();
124
- const { tip, tooltipRef, syncedPoint, isDirectHover, handleMouse, handleLeave } = useHoverSync(
125
- {
126
- data,
127
- chartId,
128
- getKey: getX,
129
- xScale,
130
- marginLeft: MARGIN.left
131
- }
116
+ }).labels,
117
+ [maxAbs, formatBottom]
118
+ );
119
+ const xLabelsAll = useMemo(() => data.map((d) => fmtAxisDate(getX(d))), [data, getX]);
120
+ const margin = useMemo(
121
+ () => autoMargin({ left: [...topLabels, ...bottomLabels], bottom: xLabelsAll }),
122
+ [topLabels, bottomLabels, xLabelsAll]
123
+ );
124
+ const xMax = Math.max(plot.width - margin.left - margin.right, 0);
125
+ const plotH = plot.height - margin.top - margin.bottom - PANE_GAP;
126
+ const topH = Math.max(Math.round(plotH * topFraction), 1);
127
+ const bottomH = Math.max(plotH - topH, 1);
128
+ const bottomTop = topH + PANE_GAP;
129
+ const innerH = topH + PANE_GAP + bottomH;
130
+ const xScale = useMemo(
131
+ () => scalePoint({ domain: data.map(getX), range: [0, xMax], padding: 0.3 }),
132
+ [data, xMax, getX]
132
133
  );
134
+ const topYScale = useMemo(
135
+ () => scaleLinear({
136
+ domain: topDomain,
137
+ range: [topH, 0],
138
+ ...useNiceTop && { nice: true }
139
+ }),
140
+ [topDomain, useNiceTop, topH]
141
+ );
142
+ const bottomYScale = useMemo(
143
+ () => scaleLinear({ domain: [-maxAbs, maxAbs], range: [bottomH, 0], nice: true }),
144
+ [maxAbs, bottomH]
145
+ );
146
+ const cursor = useChartCursor({
147
+ data,
148
+ chartId,
149
+ getKey: getX,
150
+ xScale,
151
+ marginLeft: margin.left
152
+ });
133
153
  const tickValues = useMemo(() => smartTicks(data.map(getX), xMax), [data, xMax, getX]);
134
154
  const barWidth = data.length > 0 ? Math.max(xMax / data.length * 0.6, 2) : 2;
155
+ const histogramBars = useMemo(() => {
156
+ if (!barVisible) return [];
157
+ const y0 = bottomYScale(0);
158
+ const bars = [];
159
+ for (const d of data) {
160
+ const v = getBar(d);
161
+ if (v === null || v === void 0 || Number.isNaN(v)) continue;
162
+ const cx = xScale(getX(d)) ?? 0;
163
+ const yVal = bottomYScale(v);
164
+ bars.push({
165
+ key: `bar-${getX(d)}`,
166
+ x: cx - barWidth / 2,
167
+ y: Math.min(y0, yVal),
168
+ width: barWidth,
169
+ height: Math.max(Math.abs(yVal - y0), 1),
170
+ fill: v >= 0 ? barColorPositive : barColorNegative
171
+ });
172
+ }
173
+ return bars;
174
+ }, [
175
+ barVisible,
176
+ data,
177
+ getBar,
178
+ getX,
179
+ xScale,
180
+ bottomYScale,
181
+ barWidth,
182
+ barColorPositive,
183
+ barColorNegative
184
+ ]);
135
185
  const fillPts = useMemo(() => {
136
186
  if (!fillBetween) return [];
137
- const fromLine = series.find((s) => s.key === fillBetween.from);
138
- const toLine = series.find((s) => s.key === fillBetween.to);
187
+ const fromLine = visibleSeries.find((s) => s.key === fillBetween.from);
188
+ const toLine = visibleSeries.find((s) => s.key === fillBetween.to);
139
189
  if (!fromLine || !toLine) return [];
140
190
  const out = [];
141
191
  for (const d of data) {
@@ -146,10 +196,10 @@ function DualPanelPlot(props) {
146
196
  out.push({ __d: d, __from: f, __to: t });
147
197
  }
148
198
  return out;
149
- }, [data, fillBetween, series]);
199
+ }, [data, fillBetween, visibleSeries]);
150
200
  const lineValid = useMemo(() => {
151
201
  const out = /* @__PURE__ */ new Map();
152
- for (const s of series) {
202
+ for (const s of visibleSeries) {
153
203
  const pts = [];
154
204
  for (const d of data) {
155
205
  const v = s.getValue(d);
@@ -158,12 +208,13 @@ function DualPanelPlot(props) {
158
208
  out.set(s.key, pts);
159
209
  }
160
210
  return out;
161
- }, [data, series]);
162
- const sx = syncedPoint ? xScale(getX(syncedPoint)) ?? 0 : 0;
163
- const syncedBar = syncedPoint ? getBar(syncedPoint) : null;
211
+ }, [data, visibleSeries]);
212
+ const point = cursor.point;
213
+ const sx = point !== null ? xScale(getX(point)) ?? 0 : 0;
214
+ const syncedBar = point !== null ? getBar(point) : null;
164
215
  return /* @__PURE__ */ jsxs(Fragment, { children: [
165
216
  /* @__PURE__ */ jsxs("svg", { width: plot.width, height: plot.height, children: [
166
- /* @__PURE__ */ jsxs(Group, { left: MARGIN.left, top: MARGIN.top, children: [
217
+ /* @__PURE__ */ jsxs(Group, { left: margin.left, top: margin.top, children: [
167
218
  /* @__PURE__ */ jsx(GridRows, { scale: topYScale, width: xMax, stroke: VX.grid, numTicks: 4 }),
168
219
  /* @__PURE__ */ jsx(ZoneRects, { zones: topZones, width: xMax, leftScale: topYScale }),
169
220
  fillBetween && fillPts.length > 0 && /* @__PURE__ */ jsx(
@@ -193,7 +244,7 @@ function DualPanelPlot(props) {
193
244
  },
194
245
  `top-ref-${i}`
195
246
  )),
196
- series.map((s) => {
247
+ visibleSeries.map((s) => {
197
248
  const valid = lineValid.get(s.key) ?? [];
198
249
  if (valid.length === 0) return null;
199
250
  return /* @__PURE__ */ jsx(
@@ -210,8 +261,8 @@ function DualPanelPlot(props) {
210
261
  `top-line-${s.key}`
211
262
  );
212
263
  }),
213
- syncedPoint && /* @__PURE__ */ jsx(Fragment, { children: series.map((s) => {
214
- const v = s.getValue(syncedPoint);
264
+ point !== null && /* @__PURE__ */ jsx(Fragment, { children: visibleSeries.map((s) => {
265
+ const v = s.getValue(point);
215
266
  if (v === null || v === void 0 || Number.isNaN(v)) return null;
216
267
  return /* @__PURE__ */ jsx(SeriesDot, { cx: sx, cy: topYScale(v), color: s.color }, `top-dot-${s.key}`);
217
268
  }) }),
@@ -221,12 +272,14 @@ function DualPanelPlot(props) {
221
272
  {
222
273
  width: xMax,
223
274
  height: topH + PANE_GAP,
224
- onMove: handleMouse,
225
- onLeave: handleLeave
275
+ onMove: cursor.onPointerMove,
276
+ onLeave: cursor.onPointerLeave,
277
+ onKeyDown: cursor.onKeyDown,
278
+ onBlur: cursor.onBlur
226
279
  }
227
280
  )
228
281
  ] }),
229
- /* @__PURE__ */ jsxs(Group, { left: MARGIN.left, top: MARGIN.top + bottomTop, children: [
282
+ /* @__PURE__ */ jsxs(Group, { left: margin.left, top: margin.top + bottomTop, children: [
230
283
  /* @__PURE__ */ jsx(
231
284
  "line",
232
285
  {
@@ -238,61 +291,54 @@ function DualPanelPlot(props) {
238
291
  strokeWidth: 1
239
292
  }
240
293
  ),
241
- data.map((d) => {
242
- const v = getBar(d);
243
- if (v === null || v === void 0 || Number.isNaN(v)) return null;
244
- const cx = xScale(getX(d)) ?? 0;
245
- const y0 = bottomYScale(0);
246
- const yVal = bottomYScale(v);
247
- return /* @__PURE__ */ jsx(
248
- Bar,
249
- {
250
- x: cx - barWidth / 2,
251
- y: Math.min(y0, yVal),
252
- width: barWidth,
253
- height: Math.max(Math.abs(yVal - y0), 1),
254
- fill: v >= 0 ? barColorPositive : barColorNegative,
255
- fillOpacity: 0.7,
256
- rx: 1.4
257
- },
258
- `bar-${getX(d)}`
259
- );
260
- }),
261
- syncedPoint && /* @__PURE__ */ jsx(Fragment, { children: syncedBar !== null && syncedBar !== void 0 && !Number.isNaN(syncedBar) && /* @__PURE__ */ jsx(
294
+ histogramBars.map((b) => /* @__PURE__ */ jsx(
295
+ Bar,
296
+ {
297
+ x: b.x,
298
+ y: b.y,
299
+ width: b.width,
300
+ height: b.height,
301
+ fill: b.fill,
302
+ fillOpacity: 0.7,
303
+ rx: 1.4
304
+ },
305
+ b.key
306
+ )),
307
+ barVisible && point !== null && syncedBar !== null && syncedBar !== void 0 && !Number.isNaN(syncedBar) && /* @__PURE__ */ jsx(
262
308
  SeriesDot,
263
309
  {
264
310
  cx: sx,
265
311
  cy: bottomYScale(syncedBar),
266
312
  color: syncedBar >= 0 ? barColorPositive : barColorNegative
267
313
  }
268
- ) }),
314
+ ),
269
315
  /* @__PURE__ */ jsx(AxisLeftNumeric, { scale: bottomYScale, numTicks: 3, tickFormat: formatBottom }),
270
316
  /* @__PURE__ */ jsx(AxisBottomDate, { top: bottomH, scale: xScale, tickValues }),
271
317
  /* @__PURE__ */ jsx(
272
318
  HoverOverlay,
273
319
  {
274
320
  width: xMax,
275
- height: bottomH + MARGIN.bottom,
276
- onMove: handleMouse,
277
- onLeave: handleLeave
321
+ height: bottomH + margin.bottom,
322
+ onMove: cursor.onPointerMove,
323
+ onLeave: cursor.onPointerLeave
278
324
  }
279
325
  )
280
326
  ] }),
281
- syncedPoint && /* @__PURE__ */ jsx(Group, { left: MARGIN.left, top: MARGIN.top, children: /* @__PURE__ */ jsx(Crosshair, { x: sx, top: 0, bottom: innerH }) })
327
+ point !== null && /* @__PURE__ */ jsx(Group, { left: margin.left, top: margin.top, children: /* @__PURE__ */ jsx(Crosshair, { x: sx, top: 0, bottom: innerH }) })
282
328
  ] }),
283
- /* @__PURE__ */ jsx(ChartTooltip, { tip: isDirectHover ? tip : null, tooltipRef, styles: tooltipStyles, children: tip && isDirectHover && /* @__PURE__ */ jsxs(Fragment, { children: [
329
+ cursor.isSource && point !== null && /* @__PURE__ */ jsxs(ChartTooltipFloat, { anchor: cursor.anchor, children: [
284
330
  /* @__PURE__ */ jsx(
285
331
  TooltipHeader,
286
332
  {
287
- date: getX(tip.data),
333
+ date: getX(point),
288
334
  ...(() => {
289
- const lbl = tooltipLabel?.(tip.data) ?? null;
335
+ const lbl = tooltipLabel?.(point) ?? null;
290
336
  return lbl !== null ? { label: lbl.text, labelColor: lbl.color } : {};
291
337
  })()
292
338
  }
293
339
  ),
294
340
  /* @__PURE__ */ jsxs(TooltipBody, { children: [
295
- deriveTooltipRows(series, tip.data, formatTop).map((row) => /* @__PURE__ */ jsx(
341
+ deriveTooltipRows(visibleSeries, point, formatTop).map((row) => /* @__PURE__ */ jsx(
296
342
  TooltipRow,
297
343
  {
298
344
  color: row.color,
@@ -304,8 +350,8 @@ function DualPanelPlot(props) {
304
350
  },
305
351
  row.key
306
352
  )),
307
- (() => {
308
- const v = getBar(tip.data);
353
+ barVisible && (() => {
354
+ const v = getBar(point);
309
355
  if (v === null || v === void 0 || Number.isNaN(v)) return null;
310
356
  return /* @__PURE__ */ jsx(
311
357
  TooltipRow,
@@ -318,7 +364,7 @@ function DualPanelPlot(props) {
318
364
  );
319
365
  })()
320
366
  ] })
321
- ] }) })
367
+ ] })
322
368
  ] });
323
369
  }
324
370
  const DualPanel = memo(DualPanelInner);
@@ -7,8 +7,12 @@ type HeatmapCell = {
7
7
  };
8
8
  export type HeatmapProps<T> = {
9
9
  data: T[];
10
- width: number;
11
- height: number;
10
+ /** Fixed height in pixels. Used when neither `aspectRatio` nor `fill` is set. Default 240. */
11
+ height?: number;
12
+ /** height = Math.round(containerWidth / aspectRatio). Ignored when `fill` is set. */
13
+ aspectRatio?: number;
14
+ /** Fill the parent flex/grid cell's measured height instead of a fixed/derived one. */
15
+ fill?: boolean;
12
16
  chartId: string;
13
17
  /** Extracts the y-axis category (row) from a data point. */
14
18
  getRow: (d: T) => string;
@@ -41,21 +45,21 @@ export type HeatmapProps<T> = {
41
45
  min: string;
42
46
  max: string;
43
47
  };
44
- /**
45
- * The query behind this chart hasn't resolved yet. Renders `ChartPending` (see its JSDoc for the
46
- * three-state rationale) over the full `width` × `height` box in place of the grid, suppressing
47
- * the legend strip and every cell/label/tooltip along with it. Unlike the other six kinds,
48
- * `Heatmap` doesn't compose `ChartFrame` (it takes `width`/`height` directly rather than
49
- * measuring), so this is handled locally instead of forwarded.
50
- */
48
+ /** Accessible text alternative, forwarded to `ChartFrame` as `aria-label` (+ `role="img"`). */
49
+ ariaLabel?: string;
50
+ /** Forwarded to `ChartFrame` — see `ChartPending`'s JSDoc for the three-state rationale. */
51
51
  isPending?: boolean;
52
52
  };
53
53
  /**
54
54
  * Category × category intensity grid. Each cell is a rounded rect filled with the base
55
55
  * color at an opacity derived from its value; empty cells stay a faint neutral track.
56
56
  * Generalizes argo's day-of-week × hour-of-day heatmap. Categorical axes are rendered as
57
- * plain themed `<text>` (cells are not date-categorical, so no useHoverSync / shared
58
- * cursor) — per-cell hover drives the local tooltip directly.
57
+ * plain themed `<text>` (cells are not date-categorical, so there's no shared cursor here) —
58
+ * per-cell hover drives the local tooltip directly.
59
+ *
60
+ * Composes `ChartFrame` purely for measuring (`height`/`aspectRatio`/`fill`, the same three
61
+ * sizing modes every other kind exposes) — `legend={false}` and an empty `series` opt out of
62
+ * `ChartFrame`'s own derived legend, since Heatmap already ships its own gradient strip.
59
63
  */
60
64
  declare function HeatmapInner<T>(props: HeatmapProps<T>): import("react").JSX.Element;
61
65
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"Heatmap.d.ts","sourceRoot":"","sources":["../../../src/charts/kinds/Heatmap.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAatC,kFAAkF;AAClF,KAAK,WAAW,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA;AAE9D,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI;IAC5B,IAAI,EAAE,CAAC,EAAE,CAAA;IACT,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,4DAA4D;IAC5D,MAAM,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,CAAA;IACxB,+DAA+D;IAC/D,MAAM,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,CAAA;IACxB,yCAAyC;IACzC,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,CAAA;IAC1B,+DAA+D;IAC/D,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;IACf,kEAAkE;IAClE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;IACf,yDAAyD;IACzD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,2DAA2D;IAC3D,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,MAAM,CAAA;IAClD,yCAAyC;IACzC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,kDAAkD;IAClD,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,uCAAuC;IACvC,WAAW,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAA;IACnC,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAA;IAClC,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAA;IAClC,iEAAiE;IACjE,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,SAAS,CAAA;IAChD,2EAA2E;IAC3E,MAAM,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAA;IACrC;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB,CAAA;AAgCD;;;;;;GAMG;AACH,iBAAS,YAAY,CAAC,CAAC,EAAE,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC,+BAwK9C;AAED;;;GAGG;AACH,eAAO,MAAM,OAAO,EAAyB,OAAO,YAAY,CAAA"}
1
+ {"version":3,"file":"Heatmap.d.ts","sourceRoot":"","sources":["../../../src/charts/kinds/Heatmap.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAgB,SAAS,EAAE,MAAM,OAAO,CAAA;AAWpD,kFAAkF;AAClF,KAAK,WAAW,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAA;AAK9D,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI;IAC5B,IAAI,EAAE,CAAC,EAAE,CAAA;IACT,8FAA8F;IAC9F,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,qFAAqF;IACrF,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,uFAAuF;IACvF,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,4DAA4D;IAC5D,MAAM,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,CAAA;IACxB,+DAA+D;IAC/D,MAAM,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,CAAA;IACxB,yCAAyC;IACzC,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,CAAA;IAC1B,+DAA+D;IAC/D,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;IACf,kEAAkE;IAClE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;IACf,yDAAyD;IACzD,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,2DAA2D;IAC3D,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,MAAM,CAAA;IAClD,yCAAyC;IACzC,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,kDAAkD;IAClD,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,uCAAuC;IACvC,WAAW,CAAC,EAAE,CAAC,CAAC,EAAE,MAAM,KAAK,MAAM,CAAA;IACnC,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAA;IAClC,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAA;IAClC,iEAAiE;IACjE,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,SAAS,CAAA;IAChD,2EAA2E;IAC3E,MAAM,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAA;IACrC,+FAA+F;IAC/F,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,4FAA4F;IAC5F,SAAS,CAAC,EAAE,OAAO,CAAA;CACpB,CAAA;AAgCD;;;;;;;;;;GAUG;AACH,iBAAS,YAAY,CAAC,CAAC,EAAE,KAAK,EAAE,YAAY,CAAC,CAAC,CAAC,+BAiB9C;AA6KD;;;GAGG;AACH,eAAO,MAAM,OAAO,EAAyB,OAAO,YAAY,CAAA"}
@@ -1,15 +1,13 @@
1
- import { Fragment, jsx, jsxs } from "react/jsx-runtime";
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
2
  import { Group } from "@visx/group";
3
- import { memo, useMemo } from "react";
3
+ import { memo, useMemo, useState } from "react";
4
4
  import {
5
- ChartTooltip,
5
+ ChartTooltipFloat,
6
6
  TooltipBody,
7
7
  TooltipHeader,
8
- TooltipRow,
9
- useTooltipStyles
8
+ TooltipRow
10
9
  } from "../primitives/ChartTooltip.js";
11
- import { ChartPending } from "../primitives/ChartPending.js";
12
- import { useChartTooltip } from "../hooks/useChartTooltip.js";
10
+ import { ChartFrame } from "../primitives/ChartFrame.js";
13
11
  import { VX, alpha } from "../../tokens/index.js";
14
12
  function firstSeen(data, get) {
15
13
  const seen = /* @__PURE__ */ new Set();
@@ -31,10 +29,26 @@ const PAD_TOP = 8;
31
29
  const LEGEND_H = 8;
32
30
  const LEGEND_LABEL_H = 16;
33
31
  function HeatmapInner(props) {
32
+ const { chartId, height, aspectRatio, fill, ariaLabel, isPending } = props;
33
+ return /* @__PURE__ */ jsx(
34
+ ChartFrame,
35
+ {
36
+ series: [],
37
+ chartId,
38
+ legend: false,
39
+ ...height !== void 0 && { height },
40
+ ...aspectRatio !== void 0 && { aspectRatio },
41
+ ...fill !== void 0 && { fill },
42
+ ...ariaLabel !== void 0 && { ariaLabel },
43
+ ...isPending !== void 0 && { isPending },
44
+ children: (plot) => /* @__PURE__ */ jsx(HeatmapPlot, { ...props, plot })
45
+ }
46
+ );
47
+ }
48
+ function HeatmapPlot(props) {
34
49
  const {
35
50
  data,
36
- width,
37
- height,
51
+ plot,
38
52
  chartId,
39
53
  getRow,
40
54
  getCol,
@@ -49,11 +63,10 @@ function HeatmapInner(props) {
49
63
  rowLabel = (r) => r,
50
64
  colLabel = (c) => c,
51
65
  renderTooltip,
52
- legend,
53
- isPending
66
+ legend
54
67
  } = props;
55
- const tooltipStyles = useTooltipStyles();
56
- const { tip, show, hide, tooltipRef } = useChartTooltip();
68
+ const { width, height } = plot;
69
+ const [tip, setTip] = useState(null);
57
70
  const rows = useMemo(() => rowsProp ?? firstSeen(data, getRow), [rowsProp, data, getRow]);
58
71
  const cols = useMemo(() => colsProp ?? firstSeen(data, getCol), [colsProp, data, getCol]);
59
72
  const { lookup, max } = useMemo(() => {
@@ -66,20 +79,23 @@ function HeatmapInner(props) {
66
79
  }
67
80
  return { lookup: map, max: m };
68
81
  }, [data, getRow, getCol, getValue]);
69
- if (isPending) return /* @__PURE__ */ jsx(ChartPending, { width, height });
70
82
  const legendH = legend ? LEGEND_H + LEGEND_LABEL_H : 0;
71
83
  const gridW = Math.max(0, width - PAD_LEFT);
72
84
  const gridH = Math.max(0, height - PAD_TOP - PAD_BOTTOM - legendH);
73
85
  const cellW = cols.length > 0 ? gridW / cols.length : 0;
74
86
  const cellH = rows.length > 0 ? gridH / rows.length : 0;
75
87
  const legendGradientId = `${chartId}-heat-legend`;
88
+ const show = (row, col, value, event) => {
89
+ setTip({ row, col, value, anchor: { x: event.clientX, y: event.clientY } });
90
+ };
91
+ const hide = () => setTip(null);
76
92
  return /* @__PURE__ */ jsxs("div", { style: { position: "relative" }, children: [
77
93
  /* @__PURE__ */ jsxs("svg", { width, height, children: [
78
94
  /* @__PURE__ */ jsx(Group, { left: PAD_LEFT, top: PAD_TOP, children: rows.flatMap(
79
95
  (row, ri) => cols.map((col, ci) => {
80
96
  const value = lookup.get(cellKey(row, col));
81
97
  const has = value !== void 0;
82
- const fill = has ? alpha(color, intensity(value, max)) : alpha(VX.neutral, 0.04);
98
+ const cellFill = has ? alpha(color, intensity(value, max)) : alpha(VX.neutral, 0.04);
83
99
  return /* @__PURE__ */ jsx(
84
100
  "rect",
85
101
  {
@@ -88,9 +104,9 @@ function HeatmapInner(props) {
88
104
  width: Math.max(0, cellW - cellGap),
89
105
  height: Math.max(0, cellH - cellGap),
90
106
  rx: cellRadius,
91
- fill,
107
+ fill: cellFill,
92
108
  style: { cursor: has ? "pointer" : "default" },
93
- onPointerMove: (e) => has && show({ row, col, value }, e),
109
+ onPointerMove: (e) => has && show(row, col, value, e),
94
110
  onPointerLeave: hide,
95
111
  onPointerCancel: hide
96
112
  },
@@ -156,8 +172,8 @@ function HeatmapInner(props) {
156
172
  )
157
173
  ] })
158
174
  ] }),
159
- /* @__PURE__ */ jsx(ChartTooltip, { tip, tooltipRef, styles: tooltipStyles, children: tip !== null && /* @__PURE__ */ jsxs(Fragment, { children: [
160
- /* @__PURE__ */ jsx(TooltipHeader, { date: rowLabel(tip.data.row), label: colLabel(tip.data.col) }),
175
+ tip !== null && /* @__PURE__ */ jsxs(ChartTooltipFloat, { anchor: tip.anchor, children: [
176
+ /* @__PURE__ */ jsx(TooltipHeader, { date: rowLabel(tip.row), label: colLabel(tip.col) }),
161
177
  /* @__PURE__ */ jsxs(TooltipBody, { children: [
162
178
  /* @__PURE__ */ jsx(
163
179
  TooltipRow,
@@ -165,12 +181,12 @@ function HeatmapInner(props) {
165
181
  color: alpha(color, 0.9),
166
182
  shape: "bar",
167
183
  label: "Value",
168
- value: formatValue(tip.data.value)
184
+ value: formatValue(tip.value)
169
185
  }
170
186
  ),
171
- renderTooltip?.(tip.data)
187
+ renderTooltip?.(tip)
172
188
  ] })
173
- ] }) })
189
+ ] })
174
190
  ] });
175
191
  }
176
192
  const Heatmap = memo(HeatmapInner);