basalt-ui 1.8.0 → 1.9.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/CHANGELOG.md +17 -0
- package/agent/rules/basalt-charts.md +74 -5
- package/dist/charts/hooks/useHoverSync.d.ts +18 -2
- package/dist/charts/hooks/useHoverSync.d.ts.map +1 -1
- package/dist/charts/hooks/useHoverSync.js +12 -7
- package/dist/charts/index.d.ts +1 -0
- package/dist/charts/index.d.ts.map +1 -1
- package/dist/charts/index.js +6 -0
- package/dist/charts/kinds/Bars.d.ts +2 -0
- package/dist/charts/kinds/Bars.d.ts.map +1 -1
- package/dist/charts/kinds/Bars.js +3 -1
- package/dist/charts/kinds/Donut.d.ts +2 -0
- package/dist/charts/kinds/Donut.d.ts.map +1 -1
- package/dist/charts/kinds/Donut.js +8 -4
- package/dist/charts/kinds/DualPanel.d.ts +2 -0
- package/dist/charts/kinds/DualPanel.d.ts.map +1 -1
- package/dist/charts/kinds/DualPanel.js +2 -1
- package/dist/charts/kinds/Heatmap.d.ts +8 -0
- package/dist/charts/kinds/Heatmap.d.ts.map +1 -1
- package/dist/charts/kinds/Heatmap.js +7 -3
- package/dist/charts/kinds/MultiLine.d.ts +2 -0
- package/dist/charts/kinds/MultiLine.d.ts.map +1 -1
- package/dist/charts/kinds/MultiLine.js +2 -1
- package/dist/charts/kinds/StackedArea.d.ts +2 -0
- package/dist/charts/kinds/StackedArea.d.ts.map +1 -1
- package/dist/charts/kinds/StackedArea.js +2 -1
- package/dist/charts/kinds/ZonedLine.d.ts +2 -0
- package/dist/charts/kinds/ZonedLine.d.ts.map +1 -1
- package/dist/charts/kinds/ZonedLine.js +2 -1
- package/dist/charts/primitives/Axes.d.ts +6 -1
- package/dist/charts/primitives/Axes.d.ts.map +1 -1
- package/dist/charts/primitives/Axes.js +3 -2
- package/dist/charts/primitives/ChartFrame.d.ts +8 -1
- package/dist/charts/primitives/ChartFrame.d.ts.map +1 -1
- package/dist/charts/primitives/ChartFrame.js +8 -4
- package/dist/charts/primitives/ChartPending.d.ts +34 -0
- package/dist/charts/primitives/ChartPending.d.ts.map +1 -0
- package/dist/charts/primitives/ChartPending.js +23 -0
- package/dist/charts/primitives/ChartTooltip.d.ts +5 -2
- package/dist/charts/primitives/ChartTooltip.d.ts.map +1 -1
- package/dist/charts/primitives/ChartTooltip.js +6 -1
- package/dist/charts/primitives/HoverOverlay.d.ts +5 -4
- package/dist/charts/primitives/HoverOverlay.d.ts.map +1 -1
- package/dist/charts/primitives/HoverOverlay.js +4 -2
- package/dist/cli/index.d.ts +22 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +86 -5
- package/dist/dashboard/stat-card.d.ts.map +1 -1
- package/dist/guard/index.d.ts.map +1 -1
- package/dist/guard/index.js +13 -5
- package/llms.txt +2 -2
- package/package.json +3 -5
- package/src/charts/hooks/useHoverSync.ts +43 -7
- package/src/charts/index.ts +6 -0
- package/src/charts/kinds/Bars.tsx +4 -0
- package/src/charts/kinds/Donut.tsx +10 -4
- package/src/charts/kinds/DualPanel.tsx +5 -1
- package/src/charts/kinds/Heatmap.tsx +19 -2
- package/src/charts/kinds/MultiLine.tsx +4 -1
- package/src/charts/kinds/StackedArea.tsx +4 -1
- package/src/charts/kinds/ZonedLine.tsx +4 -1
- package/src/charts/primitives/Axes.tsx +7 -1
- package/src/charts/primitives/ChartFrame.test.tsx +64 -0
- package/src/charts/primitives/ChartFrame.tsx +17 -4
- package/src/charts/primitives/ChartPending.tsx +59 -0
- package/src/charts/primitives/ChartTooltip.tsx +15 -3
- package/src/charts/primitives/HoverOverlay.tsx +12 -6
- package/src/cli/index.ts +154 -5
- package/src/cli/placement-engine.test.ts +104 -0
- package/src/dashboard/stat-card.tsx +12 -0
- package/src/guard/check-source.test.ts +20 -0
- package/src/guard/index.ts +20 -9
|
@@ -52,7 +52,8 @@ function AxisRightNumeric({
|
|
|
52
52
|
function AxisBottomDate({
|
|
53
53
|
scale,
|
|
54
54
|
top,
|
|
55
|
-
tickValues
|
|
55
|
+
tickValues,
|
|
56
|
+
tickFormat = fmtAxisDate
|
|
56
57
|
}) {
|
|
57
58
|
return /* @__PURE__ */ jsx(
|
|
58
59
|
AxisBottom,
|
|
@@ -60,7 +61,7 @@ function AxisBottomDate({
|
|
|
60
61
|
top,
|
|
61
62
|
scale,
|
|
62
63
|
tickValues,
|
|
63
|
-
tickFormat
|
|
64
|
+
tickFormat,
|
|
64
65
|
tickLabelProps: {
|
|
65
66
|
fill: VX.faint,
|
|
66
67
|
fontFamily: TICK_FONT_FAMILY,
|
|
@@ -28,6 +28,13 @@ export type ChartFrameProps = {
|
|
|
28
28
|
chartId?: string;
|
|
29
29
|
/** `false` only for the sparkline exemption — every other chart gets a legend by default. */
|
|
30
30
|
legend?: ChartFrameLegend | false;
|
|
31
|
+
/**
|
|
32
|
+
* The query behind this chart hasn't resolved yet. Renders `ChartPending` (see its JSDoc for the
|
|
33
|
+
* three-state "nothing to draw" rationale) over the full plot rect in place of `children`,
|
|
34
|
+
* suppresses the legend entirely (a legend naming a series with nothing yet to point at is its
|
|
35
|
+
* own small lie), and marks the outer container `aria-busy="true"`.
|
|
36
|
+
*/
|
|
37
|
+
isPending?: boolean;
|
|
31
38
|
/**
|
|
32
39
|
* Accessible text alternative for the chart, applied as `aria-label` (+ `role="img"`) on the
|
|
33
40
|
* outer container so screen readers announce something other than an unlabeled graphic. Every
|
|
@@ -66,5 +73,5 @@ export declare function resolveLegend(config: ChartLegendConfig | false | undefi
|
|
|
66
73
|
* Layout-only: it does not know lines from bars (that stays in the kind), so it is not a
|
|
67
74
|
* Recharts god-component. Render the child only when the resolved plot rect is non-empty.
|
|
68
75
|
*/
|
|
69
|
-
export declare function ChartFrame({ series, height, aspectRatio, fill, minWidth, chartId, legend, ariaLabel, children, }: ChartFrameProps): ReactNode;
|
|
76
|
+
export declare function ChartFrame({ series, height, aspectRatio, fill, minWidth, chartId, legend, ariaLabel, isPending, children, }: ChartFrameProps): ReactNode;
|
|
70
77
|
//# sourceMappingURL=ChartFrame.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChartFrame.d.ts","sourceRoot":"","sources":["../../../src/charts/primitives/ChartFrame.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,SAAS,EAAE,MAAM,OAAO,CAAA;AAGrD,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;
|
|
1
|
+
{"version":3,"file":"ChartFrame.d.ts","sourceRoot":"","sources":["../../../src/charts/primitives/ChartFrame.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,SAAS,EAAE,MAAM,OAAO,CAAA;AAGrD,OAAO,KAAK,EAAE,iBAAiB,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAOhF;0EAC0E;AAC1E,MAAM,MAAM,gBAAgB,GAAG;IAC7B,wBAAwB;IACxB,SAAS,CAAC,EAAE,eAAe,CAAA;IAC3B,uDAAuD;IACvD,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,4DAA4D;IAC5D,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,wDAAwD;IACxD,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAA;CAC3C,CAAA;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,uGAAuG;IACvG,MAAM,EAAE,SAAS,WAAW,EAAE,CAAA;IAC9B,8FAA8F;IAC9F,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,qFAAqF;IACrF,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,uFAAuF;IACvF,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,6EAA6E;IAC7E,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,iGAAiG;IACjG,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,6FAA6F;IAC7F,MAAM,CAAC,EAAE,gBAAgB,GAAG,KAAK,CAAA;IACjC;;;;;OAKG;IACH,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB;;;;OAIG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,oFAAoF;IACpF,QAAQ,EAAE,CAAC,IAAI,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,KAAK,SAAS,CAAA;CACjE,CAAA;AAcD;oFACoF;AACpF,MAAM,MAAM,qBAAqB,GAAG;IAClC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,WAAW,EAAE,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAA;CAC1C,CAAA;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,iBAAiB,GAAG,KAAK,GAAG,SAAS,EAC7C,KAAK,CAAC,EAAE,qBAAqB,GAC5B,gBAAgB,GAAG,KAAK,CAQ1B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,UAAU,CAAC,EACzB,MAAM,EACN,MAAM,EACN,WAAW,EACX,IAAY,EACZ,QAA4B,EAC5B,OAAO,EACP,MAAW,EACX,SAAS,EACT,SAAiB,EACjB,QAAQ,GACT,EAAE,eAAe,GAAG,SAAS,CAmD7B"}
|
|
@@ -2,6 +2,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { useChartSize } from "../hooks/useChartSize.js";
|
|
3
3
|
import { deriveLegend } from "../series.js";
|
|
4
4
|
import { ChartLegend } from "./ChartLegend.js";
|
|
5
|
+
import { ChartPending } from "./ChartPending.js";
|
|
5
6
|
const DEFAULT_HEIGHT = 240;
|
|
6
7
|
const DEFAULT_MIN_WIDTH = 200;
|
|
7
8
|
const outerStyle = (fill, vertical) => ({
|
|
@@ -32,20 +33,22 @@ function ChartFrame({
|
|
|
32
33
|
chartId,
|
|
33
34
|
legend = {},
|
|
34
35
|
ariaLabel,
|
|
36
|
+
isPending = false,
|
|
35
37
|
children
|
|
36
38
|
}) {
|
|
37
39
|
const { ref: containerRef, width: containerW, height: containerH } = useChartSize();
|
|
38
40
|
const { ref: legendRef, width: legendW, height: legendH } = useChartSize();
|
|
39
41
|
const placement = legend === false ? "bottom" : legend.placement ?? "bottom";
|
|
40
42
|
const vertical = placement === "left" || placement === "right";
|
|
43
|
+
const legendVisible = legend !== false && !isPending;
|
|
41
44
|
const resolvedHeight = fill ? containerH : aspectRatio !== void 0 ? Math.round(containerW / aspectRatio) : height ?? DEFAULT_HEIGHT;
|
|
42
|
-
const sideLegendWidth =
|
|
43
|
-
const topBottomLegendHeight =
|
|
45
|
+
const sideLegendWidth = legendVisible && vertical ? legendW : 0;
|
|
46
|
+
const topBottomLegendHeight = legendVisible && !vertical ? legendH : 0;
|
|
44
47
|
const plot = {
|
|
45
48
|
width: Math.max(containerW - sideLegendWidth, minWidth),
|
|
46
49
|
height: resolvedHeight - topBottomLegendHeight
|
|
47
50
|
};
|
|
48
|
-
const legendNode = legend === false ? null : /* @__PURE__ */ jsx("div", { ref: legendRef, style: legendWrapperStyle(vertical), children: /* @__PURE__ */ jsx(
|
|
51
|
+
const legendNode = legend === false || isPending ? null : /* @__PURE__ */ jsx("div", { ref: legendRef, style: legendWrapperStyle(vertical), children: /* @__PURE__ */ jsx(
|
|
49
52
|
ChartLegend,
|
|
50
53
|
{
|
|
51
54
|
items: deriveLegend(series),
|
|
@@ -63,9 +66,10 @@ function ChartFrame({
|
|
|
63
66
|
ref: containerRef,
|
|
64
67
|
style: outerStyle(fill, vertical),
|
|
65
68
|
...ariaLabel !== void 0 && { role: "img", "aria-label": ariaLabel },
|
|
69
|
+
...isPending && { "aria-busy": "true" },
|
|
66
70
|
children: [
|
|
67
71
|
legendNode !== null && (placement === "top" || placement === "left") && legendNode,
|
|
68
|
-
plot.width > 0 && plot.height > 0 ?
|
|
72
|
+
plot.width > 0 && plot.height > 0 && (isPending ? /* @__PURE__ */ jsx(ChartPending, { width: plot.width, height: plot.height }) : children(plot)),
|
|
69
73
|
legendNode !== null && (placement === "bottom" || placement === "right") && legendNode
|
|
70
74
|
]
|
|
71
75
|
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
export type ChartCenterProps = {
|
|
3
|
+
width: number;
|
|
4
|
+
height: number;
|
|
5
|
+
children: ReactNode;
|
|
6
|
+
};
|
|
7
|
+
/**
|
|
8
|
+
* Minimal layout primitive: centers `children` inside a `width` × `height` box. Exists because
|
|
9
|
+
* `src/charts/**` cannot import Mantine's `Flex`/`Group`/`Center` (the Mantine-free boundary), so
|
|
10
|
+
* there was previously nothing to reach for when a chart file needed to center something. Not a
|
|
11
|
+
* general layout system — width, height, children, nothing else.
|
|
12
|
+
*/
|
|
13
|
+
export declare function ChartCenter({ width, height, children }: ChartCenterProps): ReactNode;
|
|
14
|
+
export type ChartPendingProps = {
|
|
15
|
+
width: number;
|
|
16
|
+
height: number;
|
|
17
|
+
/** Default `'Loading…'`. */
|
|
18
|
+
label?: string;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* The placeholder for a chart whose data hasn't arrived yet — the third "nothing to draw" state
|
|
22
|
+
* alongside measured-and-empty and measured-and-absent (a real gap in coverage). Collapsing an
|
|
23
|
+
* in-flight query into either of those (the `data ?? []` idiom) densifies it into a fully-hatched
|
|
24
|
+
* "not measured" window: a positive claim that the series WAS watched and carried nothing, when in
|
|
25
|
+
* fact it was never asked. `ChartPending` makes "not asked yet" its own rendered state instead of
|
|
26
|
+
* borrowing one of the other two.
|
|
27
|
+
*
|
|
28
|
+
* Reserves exactly the plot's footprint and draws NOTHING that could be mistaken for a
|
|
29
|
+
* measurement — no axes, no gridlines, no hatching, no data marks — just a faint, static, centered
|
|
30
|
+
* label. No animation: the package's motion doctrine bans looping/pulsing idle motion, so a static
|
|
31
|
+
* reserved box is the correct answer here, not a compromise.
|
|
32
|
+
*/
|
|
33
|
+
export declare function ChartPending({ width, height, label }: ChartPendingProps): ReactNode;
|
|
34
|
+
//# sourceMappingURL=ChartPending.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ChartPending.d.ts","sourceRoot":"","sources":["../../../src/charts/primitives/ChartPending.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,SAAS,EAAE,MAAM,OAAO,CAAA;AAGrD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,SAAS,CAAA;CACpB,CAAA;AAUD;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,gBAAgB,GAAG,SAAS,CAEpF;AAED,MAAM,MAAM,iBAAiB,GAAG;IAC9B,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,4BAA4B;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAA;CACf,CAAA;AAOD;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,KAAkB,EAAE,EAAE,iBAAiB,GAAG,SAAS,CAMhG"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { jsx } from "react/jsx-runtime";
|
|
2
|
+
import { VX } from "../../tokens/index.js";
|
|
3
|
+
const centerStyle = (width, height) => ({
|
|
4
|
+
width,
|
|
5
|
+
height,
|
|
6
|
+
display: "flex",
|
|
7
|
+
alignItems: "center",
|
|
8
|
+
justifyContent: "center"
|
|
9
|
+
});
|
|
10
|
+
function ChartCenter({ width, height, children }) {
|
|
11
|
+
return /* @__PURE__ */ jsx("div", { style: centerStyle(width, height), children });
|
|
12
|
+
}
|
|
13
|
+
const labelStyle = {
|
|
14
|
+
color: VX.faint,
|
|
15
|
+
fontSize: VX.text.sm
|
|
16
|
+
};
|
|
17
|
+
function ChartPending({ width, height, label = "Loading\u2026" }) {
|
|
18
|
+
return /* @__PURE__ */ jsx(ChartCenter, { width, height, children: /* @__PURE__ */ jsx("span", { style: labelStyle, children: label }) });
|
|
19
|
+
}
|
|
20
|
+
export {
|
|
21
|
+
ChartCenter,
|
|
22
|
+
ChartPending
|
|
23
|
+
};
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import type { CSSProperties, ReactNode, RefObject } from 'react';
|
|
2
2
|
/** Theme-aware tooltip container styles — use with useChartTooltip(). */
|
|
3
3
|
export declare function useTooltipStyles(): CSSProperties;
|
|
4
|
-
/**
|
|
4
|
+
/**
|
|
5
|
+
* Outer tooltip shell. Renders nothing when tip is null. Renders to `document.body` via a
|
|
6
|
+
* portal, so it may be authored anywhere in the tree, including inside `<svg>`.
|
|
7
|
+
*/
|
|
5
8
|
export declare function ChartTooltip({ tip, tooltipRef, styles, children, }: {
|
|
6
9
|
tip: {
|
|
7
10
|
x: number;
|
|
@@ -10,7 +13,7 @@ export declare function ChartTooltip({ tip, tooltipRef, styles, children, }: {
|
|
|
10
13
|
tooltipRef?: RefObject<HTMLDivElement | null>;
|
|
11
14
|
styles: CSSProperties;
|
|
12
15
|
children: ReactNode;
|
|
13
|
-
}): import("react").
|
|
16
|
+
}): import("react").ReactPortal | null;
|
|
14
17
|
/** Tooltip header — shows formatted date + optional right-aligned label with color. */
|
|
15
18
|
export declare function TooltipHeader({ date, label, labelColor, }: {
|
|
16
19
|
date: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ChartTooltip.d.ts","sourceRoot":"","sources":["../../../src/charts/primitives/ChartTooltip.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"ChartTooltip.d.ts","sourceRoot":"","sources":["../../../src/charts/primitives/ChartTooltip.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAsBhE,yEAAyE;AACzE,wBAAgB,gBAAgB,IAAI,aAAa,CAEhD;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,EAC3B,GAAG,EACH,UAAU,EACV,MAAM,EACN,QAAQ,GACT,EAAE;IACD,GAAG,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;IACpC,UAAU,CAAC,EAAE,SAAS,CAAC,cAAc,GAAG,IAAI,CAAC,CAAA;IAC7C,MAAM,EAAE,aAAa,CAAA;IACrB,QAAQ,EAAE,SAAS,CAAA;CACpB,sCAeA;AAED,uFAAuF;AACvF,wBAAgB,aAAa,CAAC,EAC5B,IAAI,EACJ,KAAK,EACL,UAAU,GACX,EAAE;IACD,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,+BAoCA;AAED,4CAA4C;AAC5C,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,KAAK,EACL,KAAK,EACL,UAAU,EACV,KAAK,EACL,WAAW,EACX,MAAM,GACP,EAAE;IACD,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;IACb,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,KAAK,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,KAAK,CAAA;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,uEAAuE;IACvE,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB,+BA0CA;AAED,wBAAgB,WAAW,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,SAAS,CAAA;CAAE,+BAEhE"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { createPortal } from "react-dom";
|
|
2
3
|
import { VX } from "../../tokens/index.js";
|
|
3
4
|
import { fmtTooltipDate } from "../utils/format.js";
|
|
4
5
|
const TOOLTIP_STYLES = {
|
|
@@ -24,7 +25,11 @@ function ChartTooltip({
|
|
|
24
25
|
children
|
|
25
26
|
}) {
|
|
26
27
|
if (!tip) return null;
|
|
27
|
-
|
|
28
|
+
if (typeof document === "undefined") return null;
|
|
29
|
+
return createPortal(
|
|
30
|
+
/* @__PURE__ */ jsx("div", { ref: tooltipRef, style: { ...styles, left: tip.x, top: tip.y }, children }),
|
|
31
|
+
document.body
|
|
32
|
+
);
|
|
28
33
|
}
|
|
29
34
|
function TooltipHeader({
|
|
30
35
|
date,
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
/** Transparent <rect> that captures
|
|
1
|
+
import type { PointerEventHandler } from 'react';
|
|
2
|
+
/** Transparent <rect> that captures pointer events (mouse + touch + pen) for tooltip + crosshair
|
|
3
|
+
* sync. */
|
|
3
4
|
export declare function HoverOverlay({ width, height, onMove, onLeave, }: {
|
|
4
5
|
width: number;
|
|
5
6
|
height: number;
|
|
6
|
-
onMove:
|
|
7
|
-
onLeave:
|
|
7
|
+
onMove: PointerEventHandler<SVGRectElement>;
|
|
8
|
+
onLeave: PointerEventHandler<SVGRectElement>;
|
|
8
9
|
}): import("react").JSX.Element;
|
|
9
10
|
//# sourceMappingURL=HoverOverlay.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HoverOverlay.d.ts","sourceRoot":"","sources":["../../../src/charts/primitives/HoverOverlay.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"HoverOverlay.d.ts","sourceRoot":"","sources":["../../../src/charts/primitives/HoverOverlay.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,OAAO,CAAA;AAEhD;WACW;AACX,wBAAgB,YAAY,CAAC,EAC3B,KAAK,EACL,MAAM,EACN,MAAM,EACN,OAAO,GACR,EAAE;IACD,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,mBAAmB,CAAC,cAAc,CAAC,CAAA;IAC3C,OAAO,EAAE,mBAAmB,CAAC,cAAc,CAAC,CAAA;CAC7C,+BAeA"}
|
|
@@ -11,8 +11,10 @@ function HoverOverlay({
|
|
|
11
11
|
width,
|
|
12
12
|
height,
|
|
13
13
|
fill: "transparent",
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
style: { touchAction: "pan-y" },
|
|
15
|
+
onPointerMove: onMove,
|
|
16
|
+
onPointerLeave: onLeave,
|
|
17
|
+
onPointerCancel: onLeave
|
|
16
18
|
}
|
|
17
19
|
);
|
|
18
20
|
}
|
package/dist/cli/index.d.ts
CHANGED
|
@@ -205,8 +205,29 @@ type PeerFlags = {
|
|
|
205
205
|
hasRouter: boolean;
|
|
206
206
|
hasQuery: boolean;
|
|
207
207
|
};
|
|
208
|
+
/**
|
|
209
|
+
* Where the seed engine is running relative to the consumer's git repo — decides which
|
|
210
|
+
* repo-root-shaped seeds are safe to write. `init`/`sync` compute it once per run via
|
|
211
|
+
* `resolvePlacement` and print the matching skip notices themselves; `managedFiles` only reads it.
|
|
212
|
+
*/
|
|
213
|
+
type PlacementFlags = {
|
|
214
|
+
/** True when the package directory IS the detected repo root, or no `.git` exists anywhere above
|
|
215
|
+
* it (a consumer not using git keeps today's behaviour exactly). False means the package lives
|
|
216
|
+
* in a subdirectory of the repo (a monorepo's `web/`, say) — lefthook and GitHub Actions both
|
|
217
|
+
* only read config at the repo root, so seeding either one there is worse than not seeding at
|
|
218
|
+
* all. */
|
|
219
|
+
isPackageRepoRoot: boolean;
|
|
220
|
+
/** Absolute path of the detected repo root. Meaningful only when `isPackageRepoRoot` is false —
|
|
221
|
+
* otherwise it equals the package dir and is never read. */
|
|
222
|
+
repoRoot: string;
|
|
223
|
+
/** Absolute path of a `query-client.ts` already present elsewhere under the package's `src/` (the
|
|
224
|
+
* consumer relocated it — e.g. to `src/lib/query-client.ts`), or null. The seed's OWN
|
|
225
|
+
* destination is deliberately excluded here — that exact-path case is handled by the ordinary
|
|
226
|
+
* skip-if-exists logic every seed file already goes through, unchanged. */
|
|
227
|
+
relocatedQueryClient: string | null;
|
|
228
|
+
};
|
|
208
229
|
/** The full managed-file manifest. Stable, declarative — the single source of truth for init/sync. */
|
|
209
|
-
declare function managedFiles(peers: PeerFlags): ManagedFile[];
|
|
230
|
+
declare function managedFiles(peers: PeerFlags, placement?: PlacementFlags): ManagedFile[];
|
|
210
231
|
type Manifest = {
|
|
211
232
|
version: 1;
|
|
212
233
|
/** The basalt-ui version that last wrote this manifest — `doctor`'s one reconciliation axis. */
|
package/dist/cli/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cli/index.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/index.js
CHANGED
|
@@ -285,7 +285,59 @@ function resolvePeerFlags(cwd, flags) {
|
|
|
285
285
|
hasQuery: flags.withQuery === true || hasDependency(cwd, "@tanstack/react-query")
|
|
286
286
|
};
|
|
287
287
|
}
|
|
288
|
-
|
|
288
|
+
const ROOT_PLACEMENT = {
|
|
289
|
+
isPackageRepoRoot: true,
|
|
290
|
+
repoRoot: "",
|
|
291
|
+
relocatedQueryClient: null
|
|
292
|
+
};
|
|
293
|
+
function findRepoRoot(dir) {
|
|
294
|
+
let current = dir;
|
|
295
|
+
for (; ; ) {
|
|
296
|
+
if (existsSync(resolve(current, ".git"))) return current;
|
|
297
|
+
const parent = dirname(current);
|
|
298
|
+
if (parent === current) return dir;
|
|
299
|
+
current = parent;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
function findFileNamed(dir, filename) {
|
|
303
|
+
let entries;
|
|
304
|
+
try {
|
|
305
|
+
entries = readdirSync(dir);
|
|
306
|
+
} catch {
|
|
307
|
+
return null;
|
|
308
|
+
}
|
|
309
|
+
for (const name of entries) {
|
|
310
|
+
if (name === "node_modules" || name === ".git" || name === "dist") continue;
|
|
311
|
+
const abs = resolve(dir, name);
|
|
312
|
+
let isDir;
|
|
313
|
+
try {
|
|
314
|
+
isDir = statSync(abs).isDirectory();
|
|
315
|
+
} catch {
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
318
|
+
if (isDir) {
|
|
319
|
+
const found = findFileNamed(abs, filename);
|
|
320
|
+
if (found !== null) return found;
|
|
321
|
+
} else if (name === filename) {
|
|
322
|
+
return abs;
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
return null;
|
|
326
|
+
}
|
|
327
|
+
function findRelocatedQueryClient(cwd) {
|
|
328
|
+
const seedAbs = resolve(cwd, "src/query-client.ts");
|
|
329
|
+
const found = findFileNamed(resolve(cwd, "src"), "query-client.ts");
|
|
330
|
+
return found !== null && found !== seedAbs ? found : null;
|
|
331
|
+
}
|
|
332
|
+
function resolvePlacement(cwd) {
|
|
333
|
+
const repoRoot = findRepoRoot(cwd);
|
|
334
|
+
return {
|
|
335
|
+
isPackageRepoRoot: repoRoot === cwd,
|
|
336
|
+
repoRoot,
|
|
337
|
+
relocatedQueryClient: findRelocatedQueryClient(cwd)
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
function managedFiles(peers, placement = ROOT_PLACEMENT) {
|
|
289
341
|
const rules = RULE_NAMES.map((name) => ({
|
|
290
342
|
dest: `.claude/rules/basalt-${name}.md`,
|
|
291
343
|
mode: "managed",
|
|
@@ -370,9 +422,10 @@ function managedFiles(peers) {
|
|
|
370
422
|
}
|
|
371
423
|
};
|
|
372
424
|
const scaffolds = [];
|
|
373
|
-
if (peers.hasQuery) scaffolds.push(queryClient);
|
|
425
|
+
if (peers.hasQuery && placement.relocatedQueryClient === null) scaffolds.push(queryClient);
|
|
374
426
|
if (peers.hasRouter && peers.hasQuery) scaffolds.push(rootRoute);
|
|
375
|
-
|
|
427
|
+
const rootTooling = placement.isPackageRepoRoot ? [lefthook, ci] : [];
|
|
428
|
+
return [...rules, ...skills, claudeBlock, design, oxfmt, ...rootTooling, oxlintrc, ...scaffolds];
|
|
376
429
|
}
|
|
377
430
|
function readManifest(cwd) {
|
|
378
431
|
const raw = readIfExists(resolve(cwd, MANIFEST_PATH));
|
|
@@ -462,7 +515,8 @@ function init(cwd = process.cwd(), scaffoldFlags = {}) {
|
|
|
462
515
|
const manifest = readManifest(cwd);
|
|
463
516
|
migrateLegacyOxfmt(cwd, ctx.pkgRoot, manifest);
|
|
464
517
|
const peers = resolvePeerFlags(cwd, scaffoldFlags);
|
|
465
|
-
const
|
|
518
|
+
const placement = resolvePlacement(cwd);
|
|
519
|
+
const files = managedFiles(peers, placement);
|
|
466
520
|
let written = 0;
|
|
467
521
|
let skipped = 0;
|
|
468
522
|
const missingSources = [];
|
|
@@ -492,6 +546,19 @@ function init(cwd = process.cwd(), scaffoldFlags = {}) {
|
|
|
492
546
|
`basalt-ui init: ${missingSources.length} shipped asset(s) not present, skipped: ${missingSources.join(", ")}`
|
|
493
547
|
);
|
|
494
548
|
}
|
|
549
|
+
if (!placement.isPackageRepoRoot) {
|
|
550
|
+
console.log(
|
|
551
|
+
`basalt-ui init: skipped lefthook.yml (this package is not the repo root \u2014 repo root detected at ${placement.repoRoot}) \u2014 lefthook only reads config at the repo root; extend node_modules/basalt-ui/configs/lefthook.yml from your root lefthook.yml instead.`
|
|
552
|
+
);
|
|
553
|
+
console.log(
|
|
554
|
+
`basalt-ui init: skipped .github/workflows/check.yml (this package is not the repo root \u2014 repo root detected at ${placement.repoRoot}) \u2014 GitHub Actions only reads .github/ at the repo root; extend node_modules/basalt-ui/configs/check.yml from your root CI workflow instead.`
|
|
555
|
+
);
|
|
556
|
+
}
|
|
557
|
+
if (placement.relocatedQueryClient !== null) {
|
|
558
|
+
console.log(
|
|
559
|
+
`basalt-ui init: skipped src/query-client.ts (found an existing query client at ${relative(cwd, placement.relocatedQueryClient)}) \u2014 import it from there instead of re-seeding at the original path.`
|
|
560
|
+
);
|
|
561
|
+
}
|
|
495
562
|
if (!peers.hasQuery) {
|
|
496
563
|
console.log(
|
|
497
564
|
"basalt-ui init: skipped src/query-client.ts (no @tanstack/react-query dependency detected) \u2014 install it, or re-run with --with-query, to scaffold it."
|
|
@@ -524,7 +591,8 @@ function sync(opts = {}, cwd = process.cwd()) {
|
|
|
524
591
|
const manifest = readManifest(cwd);
|
|
525
592
|
if (!opts.check) migrateLegacyOxfmt(cwd, ctx.pkgRoot, manifest);
|
|
526
593
|
const peers = resolvePeerFlags(cwd, {});
|
|
527
|
-
const
|
|
594
|
+
const placement = resolvePlacement(cwd);
|
|
595
|
+
const files = managedFiles(peers, placement);
|
|
528
596
|
let updated = 0;
|
|
529
597
|
let recreated = 0;
|
|
530
598
|
let skippedDrift = 0;
|
|
@@ -561,6 +629,19 @@ function sync(opts = {}, cwd = process.cwd()) {
|
|
|
561
629
|
if (kind === "missing") recreated++;
|
|
562
630
|
else updated++;
|
|
563
631
|
}
|
|
632
|
+
if (!placement.isPackageRepoRoot) {
|
|
633
|
+
console.log(
|
|
634
|
+
`basalt-ui sync: skipped lefthook.yml (this package is not the repo root \u2014 repo root detected at ${placement.repoRoot}) \u2014 lefthook only reads config at the repo root; extend node_modules/basalt-ui/configs/lefthook.yml from your root lefthook.yml instead.`
|
|
635
|
+
);
|
|
636
|
+
console.log(
|
|
637
|
+
`basalt-ui sync: skipped .github/workflows/check.yml (this package is not the repo root \u2014 repo root detected at ${placement.repoRoot}) \u2014 GitHub Actions only reads .github/ at the repo root; extend node_modules/basalt-ui/configs/check.yml from your root CI workflow instead.`
|
|
638
|
+
);
|
|
639
|
+
}
|
|
640
|
+
if (placement.relocatedQueryClient !== null) {
|
|
641
|
+
console.log(
|
|
642
|
+
`basalt-ui sync: skipped src/query-client.ts (found an existing query client at ${relative(cwd, placement.relocatedQueryClient)}) \u2014 import it from there instead of re-seeding at the original path.`
|
|
643
|
+
);
|
|
644
|
+
}
|
|
564
645
|
if (opts.check) {
|
|
565
646
|
if (driftLines.length > 0) {
|
|
566
647
|
console.error("basalt-ui sync --check: locally-drifted managed files:");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stat-card.d.ts","sourceRoot":"","sources":["../../src/dashboard/stat-card.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"stat-card.d.ts","sourceRoot":"","sources":["../../src/dashboard/stat-card.tsx"],"names":[],"mappings":"AAsDA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAItC,sGAAsG;AACtG,MAAM,MAAM,YAAY,GAAG,MAAM,GAAG,MAAM,GAAG,KAAK,CAAA;AAYlD,MAAM,MAAM,aAAa,GAAG;IAC1B,2FAA2F;IAC3F,KAAK,EAAE,MAAM,CAAA;IACb,oEAAoE;IACpE,KAAK,EAAE,MAAM,CAAA;IACb,oFAAoF;IACpF,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,oGAAoG;IACpG,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;kGAC8F;IAC9F,SAAS,CAAC,EAAE,SAAS,CAAA;IACrB,qFAAqF;IACrF,IAAI,CAAC,EAAE,SAAS,CAAA;IAChB;;;+EAG2E;IAC3E,IAAI,CAAC,EAAE,YAAY,CAAA;CACpB,CAAA;AAED,wBAAgB,QAAQ,CAAC,EACvB,KAAK,EACL,KAAK,EACL,KAAK,EACL,WAAW,EACX,SAAS,EACT,IAAI,EACJ,IAAI,GACL,EAAE,aAAa,+BAyFf"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/guard/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAE7E,YAAY,EAAE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,aAAa,EAAE,CAAA;AAoP9D,8EAA8E;AAC9E,eAAO,MAAM,oBAAoB,EAAE,WAoBlC,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/guard/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAE7E,YAAY,EAAE,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,aAAa,EAAE,CAAA;AAoP9D,8EAA8E;AAC9E,eAAO,MAAM,oBAAoB,EAAE,WAoBlC,CAAA;AA0TD;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;gCA0BL,WAAW;;;;;;;gCAcX,WAAW;;;;;gCAMX,WAAW;;;;;;;gCAaX,WAAW;;;;;;;gCAQX,WAAW;;;;;;gCAOX,WAAW;;;;;;;gCASX,WAAW;;;;;;gCAOX,WAAW;;;;;;;gCAaX,WAAW;;;;;;gCAMX,WAAW;;;;;;;gCAaX,WAAW;;;;;;;gCAUX,WAAW;;;;;;;gCAUX,WAAW;;;;;;;gCASX,WAAW;;;;;;;gCASX,WAAW;;;;;;;gCAUX,WAAW;;;;CAOmB,CAAA;AAUjD;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,WAAW,GAAG,OAAO,EAAE,CA2PtF"}
|
package/dist/guard/index.js
CHANGED
|
@@ -78,7 +78,7 @@ const DEFAULT_GUARD_CONFIG = {
|
|
|
78
78
|
severity: {}
|
|
79
79
|
};
|
|
80
80
|
const GRACE_PERIOD_KINDS = {
|
|
81
|
-
"mantine-shade-index": "introduced 1.7.0 \u2014 promote to error in 1.
|
|
81
|
+
"mantine-shade-index": "introduced 1.7.0 \u2014 promote to error in 1.10.0"
|
|
82
82
|
};
|
|
83
83
|
function severityOf(kind, cfg) {
|
|
84
84
|
return cfg.severity?.[kind] ?? (kind in GRACE_PERIOD_KINDS ? "warn" : "error");
|
|
@@ -339,8 +339,12 @@ const GUARD_RULES = {
|
|
|
339
339
|
pattern: RAW_HTML_TAG,
|
|
340
340
|
// handled inline (3-condition conjunction); entry keeps registry complete
|
|
341
341
|
enabled: (cfg) => cfg.rawHtmlLayout,
|
|
342
|
-
// JSX-tag-shaped (`<div style={{...}}>`) — never appears in CSS text.
|
|
343
|
-
|
|
342
|
+
// JSX-tag-shaped (`<div style={{...}}>`) — never appears in CSS text. Also excluded from
|
|
343
|
+
// chart files: the remedy (Box/Flex/Grid/Stack/Group) is `@mantine/*`, and src/charts/** is a
|
|
344
|
+
// lint-enforced Mantine-free layer (basalt/token-layer-boundary) — so inside a chart file the
|
|
345
|
+
// finding is unactionable, not merely inconvenient, and the only "fix" would be a theme-allow
|
|
346
|
+
// comment written inside the very directory this rule protects.
|
|
347
|
+
appliesTo: (relPath) => !isChartFile(relPath) && !relPath.endsWith(".css"),
|
|
344
348
|
message: "Raw HTML element with inline layout/surface styling \u2014 use a Mantine layout primitive (Box/Flex/Grid/Stack/Group)."
|
|
345
349
|
},
|
|
346
350
|
"inline-spacing": {
|
|
@@ -354,8 +358,12 @@ const GUARD_RULES = {
|
|
|
354
358
|
pattern: INLINE_DISPLAY,
|
|
355
359
|
enabled: (cfg) => cfg.inlineDisplay,
|
|
356
360
|
// JSX-object-shaped (`display: 'flex'`, quoted value) — CSS never quotes a keyword value
|
|
357
|
-
// (`display: flex;` is bare), so the pattern's quote anchor can never match real CSS.
|
|
358
|
-
|
|
361
|
+
// (`display: flex;` is bare), so the pattern's quote anchor can never match real CSS. Also
|
|
362
|
+
// excluded from chart files: the remedy (Flex/Grid/Group) is `@mantine/*`, and src/charts/**
|
|
363
|
+
// is a lint-enforced Mantine-free layer (basalt/token-layer-boundary) — so inside a chart file
|
|
364
|
+
// the finding is unactionable, not merely inconvenient, and the only "fix" would be a
|
|
365
|
+
// theme-allow comment written inside the very directory this rule protects.
|
|
366
|
+
appliesTo: (relPath) => !isChartFile(relPath) && !relPath.endsWith(".css"),
|
|
359
367
|
message: "Use <Flex>/<Grid>/<Group> instead of an inline display:flex/grid."
|
|
360
368
|
},
|
|
361
369
|
"raw-visx-axis": {
|
package/llms.txt
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# basalt-ui v1.
|
|
1
|
+
# basalt-ui v1.9.0
|
|
2
2
|
# Opinionated framework for Mantine-based React apps — theme, app shell, visx charts, toolchain presets
|
|
3
3
|
#
|
|
4
4
|
# basalt-ui is an opinionated framework for Mantine-based React apps.
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
# ./tokens.css subpath. Guide: docs/FRAMEWORK-FREE.md in the repo.
|
|
20
20
|
|
|
21
21
|
Package: basalt-ui
|
|
22
|
-
Version: 1.
|
|
22
|
+
Version: 1.9.0
|
|
23
23
|
|
|
24
24
|
## basalt-ui
|
|
25
25
|
Import: import { ... } from 'basalt-ui'
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "basalt-ui",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.0",
|
|
4
4
|
"description": "Opinionated framework for Mantine-based React apps — theme, app shell, visx charts, toolchain presets",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"charts",
|
|
@@ -342,12 +342,10 @@
|
|
|
342
342
|
"primitives"
|
|
343
343
|
],
|
|
344
344
|
"inline-display": [
|
|
345
|
-
"agent"
|
|
346
|
-
"primitives"
|
|
345
|
+
"agent"
|
|
347
346
|
],
|
|
348
347
|
"raw-html-layout": [
|
|
349
|
-
"agent"
|
|
350
|
-
"primitives"
|
|
348
|
+
"agent"
|
|
351
349
|
]
|
|
352
350
|
}
|
|
353
351
|
}
|
|
@@ -24,12 +24,29 @@ export function useHoverSync<T>({
|
|
|
24
24
|
getKey,
|
|
25
25
|
xScale,
|
|
26
26
|
marginLeft,
|
|
27
|
+
resolveKey,
|
|
27
28
|
}: {
|
|
28
29
|
data: T[]
|
|
29
30
|
chartId: string
|
|
30
31
|
getKey: (d: T) => string
|
|
31
32
|
xScale: XScale
|
|
32
33
|
marginLeft: number
|
|
34
|
+
/**
|
|
35
|
+
* Overrides how a SIBLING's broadcast key resolves to one of THIS chart's own points. Only
|
|
36
|
+
* affects reading another chart's hover — this chart's own hover (via `getKey`/`xScale`) is
|
|
37
|
+
* untouched.
|
|
38
|
+
*
|
|
39
|
+
* Cross-chart sync is exact string match by default (`pointByKey.get(ctx.key)`), so two charts
|
|
40
|
+
* share a cursor only if they emit identical key strings. A chart that downsamples/folds its
|
|
41
|
+
* domain to fit a narrow viewport (e.g. ~288 buckets folded into ~97 columns at 390px) no longer
|
|
42
|
+
* owns most of the keys its unfolded siblings broadcast — the shared crosshair then appears on
|
|
43
|
+
* roughly one hover in three, with no rule a reader can infer, which is worse than no shared
|
|
44
|
+
* cursor at all. Pass `resolveKey` to map a foreign key onto whichever of this chart's own points
|
|
45
|
+
* swallowed it (e.g. the folded bucket the key falls into).
|
|
46
|
+
*
|
|
47
|
+
* Omit it and behaviour is byte-identical to today.
|
|
48
|
+
*/
|
|
49
|
+
resolveKey?: (key: string) => T | null
|
|
33
50
|
}) {
|
|
34
51
|
const ctx = useContext(HoverContext)
|
|
35
52
|
// Latest context in a ref so the mouse callbacks stay referentially stable: the provider's value
|
|
@@ -53,13 +70,26 @@ export function useHoverSync<T>({
|
|
|
53
70
|
|
|
54
71
|
const { tip, show, hide, tooltipRef, lastDateRef } = useChartTooltip<T>()
|
|
55
72
|
|
|
73
|
+
// Latest accessors in refs, same reasoning as `ctxRef` above: the natural call style is an
|
|
74
|
+
// inline arrow for `getKey`/`xScale`, which is fresh on every render, and this file already
|
|
75
|
+
// pays for that hazard once (see the comment above `ctxRef`) without applying the fix here — a
|
|
76
|
+
// several-hundred-entry `pointByKey` Map was rebuilding on every render of a page that
|
|
77
|
+
// re-renders on a heartbeat. Trade-off: because `getKey` is read through a ref, changing key
|
|
78
|
+
// SEMANTICS without changing `data` will NOT rebuild the map. That's the intended trade — a
|
|
79
|
+
// `getKey` that changes meaning for the same data is pathological; a `getKey` that changes
|
|
80
|
+
// identity every render is the normal case.
|
|
81
|
+
const getKeyRef = useRef(getKey)
|
|
82
|
+
getKeyRef.current = getKey
|
|
83
|
+
const xScaleRef = useRef(xScale)
|
|
84
|
+
xScaleRef.current = xScale
|
|
85
|
+
|
|
56
86
|
// O(1) lookup of a point by its key. Under a provider every sibling resolves the broadcast
|
|
57
87
|
// key each frame; an O(n) `data.find` per sibling per move is N×O(M). This makes it N×O(1).
|
|
58
88
|
const pointByKey = useMemo(() => {
|
|
59
89
|
const m = new Map<string, T>()
|
|
60
|
-
for (const d of data) m.set(
|
|
90
|
+
for (const d of data) m.set(getKeyRef.current(d), d)
|
|
61
91
|
return m
|
|
62
|
-
}, [data
|
|
92
|
+
}, [data])
|
|
63
93
|
|
|
64
94
|
const handleMouse = useCallback(
|
|
65
95
|
(event: React.MouseEvent<SVGRectElement>) => {
|
|
@@ -69,7 +99,7 @@ export function useHoverSync<T>({
|
|
|
69
99
|
let closest: T = data[0] as T
|
|
70
100
|
let minDist = Infinity
|
|
71
101
|
for (const d of data) {
|
|
72
|
-
const sx =
|
|
102
|
+
const sx = xScaleRef.current(getKeyRef.current(d)) ?? 0
|
|
73
103
|
const dist = Math.abs(sx - px)
|
|
74
104
|
if (dist < minDist) {
|
|
75
105
|
minDist = dist
|
|
@@ -77,13 +107,13 @@ export function useHoverSync<T>({
|
|
|
77
107
|
}
|
|
78
108
|
}
|
|
79
109
|
show(closest, event)
|
|
80
|
-
const key =
|
|
110
|
+
const key = getKeyRef.current(closest)
|
|
81
111
|
if (lastDateRef.current !== key) {
|
|
82
112
|
lastDateRef.current = key
|
|
83
113
|
ctxRef.current.setHover(key, chartId)
|
|
84
114
|
}
|
|
85
115
|
},
|
|
86
|
-
[data,
|
|
116
|
+
[data, chartId, marginLeft, show, lastDateRef],
|
|
87
117
|
)
|
|
88
118
|
|
|
89
119
|
const handleLeave = useCallback(() => {
|
|
@@ -97,8 +127,14 @@ export function useHoverSync<T>({
|
|
|
97
127
|
// The point the crosshair + synced dots track. Inside a <ChartHoverSync> this follows the
|
|
98
128
|
// broadcast key (so every sibling paints a ghost crosshair at the same x). WITHOUT a provider the
|
|
99
129
|
// chart is standalone, so it falls back to THIS chart's own hovered point (`tip.data`) — otherwise
|
|
100
|
-
// a chart outside a provider would never draw a crosshair/dots at all.
|
|
101
|
-
|
|
130
|
+
// a chart outside a provider would never draw a crosshair/dots at all. When `resolveKey` is
|
|
131
|
+
// supplied it replaces the default exact-match lookup for reading a SIBLING's broadcast key (see
|
|
132
|
+
// its doc comment above) — this chart's own hover is unaffected either way.
|
|
133
|
+
const syncedPoint = ctx.key
|
|
134
|
+
? resolveKey
|
|
135
|
+
? resolveKey(ctx.key)
|
|
136
|
+
: (pointByKey.get(ctx.key) ?? null)
|
|
137
|
+
: (tip?.data ?? null)
|
|
102
138
|
// Which chart owns the floating tooltip. Inside a provider the hovered chart is the one whose id
|
|
103
139
|
// matches the broadcast source (siblings show a ghost crosshair only). Standalone, its own local
|
|
104
140
|
// hover (`tip`) drives the tooltip.
|