basalt-ui 1.13.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.
- package/AGENTS.md +1 -1
- package/CHANGELOG.md +15 -0
- package/README.md +38 -2
- package/agent/rules/basalt-charts.md +260 -108
- package/agent/rules/basalt-mantine.md +4 -2
- package/agent/skills/basalt-charts/SKILL.md +114 -64
- package/agent/skills/basalt-design/SKILL.md +4 -3
- package/configs/oxlint-plugin.js +152 -0
- package/configs/oxlint-plugin.test.ts +107 -0
- package/configs/oxlint.json +2 -0
- package/dist/charts/cursor/resolve.d.ts +33 -0
- package/dist/charts/cursor/resolve.d.ts.map +1 -0
- package/dist/charts/cursor/resolve.js +61 -0
- package/dist/charts/cursor/resolve.test.d.ts +2 -0
- package/dist/charts/cursor/resolve.test.d.ts.map +1 -0
- package/dist/charts/cursor/scope.d.ts +17 -0
- package/dist/charts/cursor/scope.d.ts.map +1 -0
- package/dist/charts/cursor/scope.js +20 -0
- package/dist/charts/cursor/store.d.ts +31 -0
- package/dist/charts/cursor/store.d.ts.map +1 -0
- package/dist/charts/cursor/store.js +25 -0
- package/dist/charts/cursor/store.test.d.ts +2 -0
- package/dist/charts/cursor/store.test.d.ts.map +1 -0
- package/dist/charts/hooks/useChartCursor.d.ts +39 -0
- package/dist/charts/hooks/useChartCursor.d.ts.map +1 -0
- package/dist/charts/hooks/useChartCursor.js +104 -0
- package/dist/charts/hooks/useChartSize.d.ts +8 -4
- package/dist/charts/hooks/useChartSize.d.ts.map +1 -1
- package/dist/charts/index.d.ts +16 -13
- package/dist/charts/index.d.ts.map +1 -1
- package/dist/charts/index.js +38 -19
- package/dist/charts/kinds/Bars.d.ts +25 -47
- package/dist/charts/kinds/Bars.d.ts.map +1 -1
- package/dist/charts/kinds/Bars.js +230 -390
- package/dist/charts/kinds/Donut.d.ts +5 -4
- package/dist/charts/kinds/Donut.d.ts.map +1 -1
- package/dist/charts/kinds/Donut.js +17 -14
- package/dist/charts/kinds/DualPanel.d.ts.map +1 -1
- package/dist/charts/kinds/DualPanel.js +136 -90
- package/dist/charts/kinds/Heatmap.d.ts +15 -11
- package/dist/charts/kinds/Heatmap.d.ts.map +1 -1
- package/dist/charts/kinds/Heatmap.js +38 -22
- package/dist/charts/kinds/MultiLine.d.ts +27 -35
- package/dist/charts/kinds/MultiLine.d.ts.map +1 -1
- package/dist/charts/kinds/MultiLine.js +114 -237
- package/dist/charts/kinds/StackedArea.d.ts +21 -10
- package/dist/charts/kinds/StackedArea.d.ts.map +1 -1
- package/dist/charts/kinds/StackedArea.js +74 -132
- package/dist/charts/kinds/ZonedLine.d.ts +24 -40
- package/dist/charts/kinds/ZonedLine.d.ts.map +1 -1
- package/dist/charts/kinds/ZonedLine.js +103 -184
- package/dist/charts/layout/auto-margin.d.ts +60 -0
- package/dist/charts/layout/auto-margin.d.ts.map +1 -0
- package/dist/charts/layout/auto-margin.js +37 -0
- package/dist/charts/layout/auto-margin.test.d.ts +2 -0
- package/dist/charts/layout/auto-margin.test.d.ts.map +1 -0
- package/dist/charts/primitives/CartesianChart.d.ts +143 -0
- package/dist/charts/primitives/CartesianChart.d.ts.map +1 -0
- package/dist/charts/primitives/CartesianChart.js +349 -0
- package/dist/charts/primitives/CartesianChart.test.d.ts +2 -0
- package/dist/charts/primitives/CartesianChart.test.d.ts.map +1 -0
- package/dist/charts/primitives/ChartFrame.d.ts +16 -5
- package/dist/charts/primitives/ChartFrame.d.ts.map +1 -1
- package/dist/charts/primitives/ChartFrame.js +17 -2
- package/dist/charts/primitives/ChartLegend.d.ts +12 -1
- package/dist/charts/primitives/ChartLegend.d.ts.map +1 -1
- package/dist/charts/primitives/ChartLegend.js +21 -3
- package/dist/charts/primitives/ChartLegend.test.d.ts +2 -0
- package/dist/charts/primitives/ChartLegend.test.d.ts.map +1 -0
- package/dist/charts/primitives/ChartTooltip.d.ts +16 -16
- package/dist/charts/primitives/ChartTooltip.d.ts.map +1 -1
- package/dist/charts/primitives/ChartTooltip.js +45 -19
- package/dist/charts/primitives/HoverOverlay.d.ts +19 -4
- package/dist/charts/primitives/HoverOverlay.d.ts.map +1 -1
- package/dist/charts/primitives/HoverOverlay.js +22 -1
- package/dist/charts/primitives/XZoneRects.d.ts +40 -0
- package/dist/charts/primitives/XZoneRects.d.ts.map +1 -0
- package/dist/charts/primitives/XZoneRects.js +28 -0
- package/dist/charts/primitives/XZoneRects.test.d.ts +2 -0
- package/dist/charts/primitives/XZoneRects.test.d.ts.map +1 -0
- package/dist/charts/series.d.ts +16 -1
- package/dist/charts/series.d.ts.map +1 -1
- package/dist/charts/series.js +3 -1
- package/dist/charts/series.test.d.ts +2 -0
- package/dist/charts/series.test.d.ts.map +1 -0
- package/dist/charts/utils/measure-text.d.ts +25 -0
- package/dist/charts/utils/measure-text.d.ts.map +1 -0
- package/dist/charts/utils/measure-text.js +64 -0
- package/dist/charts/utils/measure-text.test.d.ts +2 -0
- package/dist/charts/utils/measure-text.test.d.ts.map +1 -0
- package/dist/content/fence-block.d.ts.map +1 -1
- package/dist/shell/app-sidebar.d.ts +19 -1
- package/dist/shell/app-sidebar.d.ts.map +1 -1
- package/dist/shell/app-sidebar.js +30 -26
- package/dist/shell/app-sidebar.module.css +11 -0
- package/dist/shell/app-sidebar.module.css.d.ts +1 -0
- package/dist/shell/app-sidebar.test.d.ts +2 -0
- package/dist/shell/app-sidebar.test.d.ts.map +1 -0
- package/dist/shell/index.d.ts +10 -1
- package/dist/shell/index.d.ts.map +1 -1
- package/dist/shell/index.js +2 -0
- package/dist/shell/index.test.d.ts +2 -0
- package/dist/shell/index.test.d.ts.map +1 -0
- package/dist/surfaces.d.ts +1 -1
- package/dist/surfaces.d.ts.map +1 -1
- package/dist/surfaces.js +1 -1
- package/dist/tokens/chart-margin.test.d.ts +2 -0
- package/dist/tokens/chart-margin.test.d.ts.map +1 -0
- package/dist/tokens/index.d.ts +29 -0
- package/dist/tokens/index.d.ts.map +1 -1
- package/dist/tokens/index.js +10 -0
- package/dist/tokens/palette.d.ts +4 -0
- package/dist/tokens/palette.d.ts.map +1 -1
- package/dist/tokens/palette.js +3 -0
- package/llms.txt +3 -3
- package/package.json +1 -1
- package/src/charts/cursor/resolve.test.ts +61 -0
- package/src/charts/cursor/resolve.ts +102 -0
- package/src/charts/cursor/scope.tsx +32 -0
- package/src/charts/cursor/store.test.ts +46 -0
- package/src/charts/cursor/store.ts +53 -0
- package/src/charts/hooks/useChartCursor.ts +163 -0
- package/src/charts/hooks/useChartSize.ts +8 -4
- package/src/charts/index.ts +34 -14
- package/src/charts/kinds/Bars.tsx +327 -460
- package/src/charts/kinds/Donut.tsx +31 -23
- package/src/charts/kinds/DualPanel.tsx +228 -134
- package/src/charts/kinds/Heatmap.tsx +72 -47
- package/src/charts/kinds/MultiLine.tsx +158 -294
- package/src/charts/kinds/StackedArea.tsx +116 -175
- package/src/charts/kinds/ZonedLine.tsx +148 -255
- package/src/charts/layout/auto-margin.test.ts +42 -0
- package/src/charts/layout/auto-margin.ts +110 -0
- package/src/charts/primitives/CartesianChart.test.ts +99 -0
- package/src/charts/primitives/CartesianChart.tsx +566 -0
- package/src/charts/primitives/ChartFrame.test.tsx +33 -0
- package/src/charts/primitives/ChartFrame.tsx +40 -4
- package/src/charts/primitives/ChartLegend.test.tsx +48 -0
- package/src/charts/primitives/ChartLegend.tsx +45 -2
- package/src/charts/primitives/ChartTooltip.tsx +67 -37
- package/src/charts/primitives/HoverOverlay.tsx +39 -3
- package/src/charts/primitives/XZoneRects.test.tsx +63 -0
- package/src/charts/primitives/XZoneRects.tsx +67 -0
- package/src/charts/series.test.ts +61 -0
- package/src/charts/series.ts +18 -1
- package/src/charts/utils/measure-text.test.ts +36 -0
- package/src/charts/utils/measure-text.ts +109 -0
- package/src/content/fence-block.tsx +2 -3
- package/src/shell/app-sidebar.module.css +11 -0
- package/src/shell/app-sidebar.module.css.d.ts +1 -0
- package/src/shell/app-sidebar.test.tsx +98 -0
- package/src/shell/app-sidebar.tsx +22 -0
- package/src/shell/index.test.tsx +35 -0
- package/src/shell/index.tsx +11 -0
- package/src/surfaces.ts +2 -1
- package/src/theme/spacing.test.ts +2 -0
- package/src/tokens/chart-margin.test.ts +39 -0
- package/src/tokens/density.test.ts +1 -0
- package/src/tokens/index.ts +36 -0
- package/src/tokens/palette.ts +3 -0
- package/dist/charts/hooks/useChartTooltip.d.ts +0 -13
- package/dist/charts/hooks/useChartTooltip.d.ts.map +0 -1
- package/dist/charts/hooks/useChartTooltip.js +0 -26
- package/dist/charts/hooks/useHoverSync.d.ts +0 -46
- package/dist/charts/hooks/useHoverSync.d.ts.map +0 -1
- package/dist/charts/hooks/useHoverSync.js +0 -75
- package/dist/charts/hover-context.d.ts +0 -21
- package/dist/charts/hover-context.d.ts.map +0 -1
- package/dist/charts/hover-context.js +0 -12
- package/dist/charts/hover-sync.d.ts +0 -14
- package/dist/charts/hover-sync.d.ts.map +0 -1
- package/dist/charts/hover-sync.js +0 -17
- package/dist/charts/primitives/ResponsiveChart.d.ts +0 -45
- package/dist/charts/primitives/ResponsiveChart.d.ts.map +0 -1
- package/dist/charts/primitives/ResponsiveChart.js +0 -24
- package/src/charts/hooks/useChartTooltip.ts +0 -31
- package/src/charts/hooks/useHoverSync.ts +0 -152
- package/src/charts/hover-context.ts +0 -28
- package/src/charts/hover-sync.tsx +0 -37
- 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
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
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,
|
|
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 {
|
|
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
|
|
43
|
-
const
|
|
44
|
-
const
|
|
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(() =>
|
|
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(
|
|
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.
|
|
143
|
-
label: seriesLabel(tip.
|
|
144
|
-
value: formatValue(tip.
|
|
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.
|
|
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":"
|
|
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
|
-
|
|
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 {
|
|
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
|
|
73
|
-
const
|
|
74
|
-
const
|
|
75
|
-
const
|
|
76
|
-
const
|
|
77
|
-
|
|
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
|
|
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
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
const
|
|
109
|
-
let
|
|
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 >
|
|
106
|
+
if (a > m) m = a;
|
|
115
107
|
}
|
|
116
|
-
|
|
117
|
-
|
|
108
|
+
return m === 0 ? 1 : m;
|
|
109
|
+
}, [data, getBar]);
|
|
110
|
+
const bottomLabels = useMemo(
|
|
111
|
+
() => probeAxisLabels({
|
|
118
112
|
domain: [-maxAbs, maxAbs],
|
|
119
|
-
|
|
113
|
+
ticks: 3,
|
|
114
|
+
format: formatBottom,
|
|
120
115
|
nice: true
|
|
121
|
-
})
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
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 =
|
|
138
|
-
const toLine =
|
|
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,
|
|
199
|
+
}, [data, fillBetween, visibleSeries]);
|
|
150
200
|
const lineValid = useMemo(() => {
|
|
151
201
|
const out = /* @__PURE__ */ new Map();
|
|
152
|
-
for (const s of
|
|
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,
|
|
162
|
-
const
|
|
163
|
-
const
|
|
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:
|
|
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
|
-
|
|
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
|
-
|
|
214
|
-
const v = s.getValue(
|
|
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:
|
|
225
|
-
onLeave:
|
|
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:
|
|
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
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
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 +
|
|
276
|
-
onMove:
|
|
277
|
-
onLeave:
|
|
321
|
+
height: bottomH + margin.bottom,
|
|
322
|
+
onMove: cursor.onPointerMove,
|
|
323
|
+
onLeave: cursor.onPointerLeave
|
|
278
324
|
}
|
|
279
325
|
)
|
|
280
326
|
] }),
|
|
281
|
-
|
|
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
|
-
|
|
329
|
+
cursor.isSource && point !== null && /* @__PURE__ */ jsxs(ChartTooltipFloat, { anchor: cursor.anchor, children: [
|
|
284
330
|
/* @__PURE__ */ jsx(
|
|
285
331
|
TooltipHeader,
|
|
286
332
|
{
|
|
287
|
-
date: getX(
|
|
333
|
+
date: getX(point),
|
|
288
334
|
...(() => {
|
|
289
|
-
const lbl = tooltipLabel?.(
|
|
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(
|
|
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(
|
|
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
|
-
|
|
11
|
-
height
|
|
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
|
-
|
|
46
|
-
|
|
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
|
|
58
|
-
*
|
|
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,
|
|
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 {
|
|
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
|
-
|
|
5
|
+
ChartTooltipFloat,
|
|
6
6
|
TooltipBody,
|
|
7
7
|
TooltipHeader,
|
|
8
|
-
TooltipRow
|
|
9
|
-
useTooltipStyles
|
|
8
|
+
TooltipRow
|
|
10
9
|
} from "../primitives/ChartTooltip.js";
|
|
11
|
-
import {
|
|
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
|
-
|
|
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
|
|
56
|
-
const
|
|
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
|
|
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(
|
|
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
|
-
|
|
160
|
-
/* @__PURE__ */ jsx(TooltipHeader, { date: rowLabel(tip.
|
|
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.
|
|
184
|
+
value: formatValue(tip.value)
|
|
169
185
|
}
|
|
170
186
|
),
|
|
171
|
-
renderTooltip?.(tip
|
|
187
|
+
renderTooltip?.(tip)
|
|
172
188
|
] })
|
|
173
|
-
] })
|
|
189
|
+
] })
|
|
174
190
|
] });
|
|
175
191
|
}
|
|
176
192
|
const Heatmap = memo(HeatmapInner);
|