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.
- package/AGENTS.md +1 -1
- package/CHANGELOG.md +8 -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/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/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
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"fence-block.d.ts","sourceRoot":"","sources":["../../src/content/fence-block.tsx"],"names":[],"mappings":"AAgCA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACtC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AAOnC,MAAM,MAAM,kBAAkB,GAAG;IAC/B,6DAA6D;IAC7D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,2FAA2F;IAC3F,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B,0FAA0F;IAC1F,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB,+EAA+E;IAC/E,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,oGAAoG;IACpG,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;CAC1B,CAAA;AAED;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,GAAG,EAAE,kBAAkB,KAAK,SAAS,CAAA;AAElE,yCAAyC;AACzC,MAAM,MAAM,cAAc,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC,CAAA;AAYpE;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,aAAa,GAAG,aAAa,CAEhE;AAED,uFAAuF;AACvF,eAAO,MAAM,wBAAwB,EAAE,cAEtC,CAAA;AAiBD,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAA;IACvB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,uEAAuE;IACvE,QAAQ,CAAC,SAAS,CAAC,EAAE,cAAc,CAAA;CACpC,CAAA;
|
|
1
|
+
{"version":3,"file":"fence-block.d.ts","sourceRoot":"","sources":["../../src/content/fence-block.tsx"],"names":[],"mappings":"AAgCA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACtC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AAOnC,MAAM,MAAM,kBAAkB,GAAG;IAC/B,6DAA6D;IAC7D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,2FAA2F;IAC3F,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;IAC1B,0FAA0F;IAC1F,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB,+EAA+E;IAC/E,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;IACvB,oGAAoG;IACpG,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;CAC1B,CAAA;AAED;;;;;;GAMG;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,GAAG,EAAE,kBAAkB,KAAK,SAAS,CAAA;AAElE,yCAAyC;AACzC,MAAM,MAAM,cAAc,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC,CAAA;AAYpE;;;;;;;;;;GAUG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,aAAa,GAAG,aAAa,CAEhE;AAED,uFAAuF;AACvF,eAAO,MAAM,wBAAwB,EAAE,cAEtC,CAAA;AAiBD,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,CAAA;IACvB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,uEAAuE;IACvE,QAAQ,CAAC,SAAS,CAAC,EAAE,cAAc,CAAA;CACpC,CAAA;AA0HD,wBAAgB,UAAU,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,SAAS,EAAE,EAAE,eAAe,sCA2BvE"}
|
package/dist/surfaces.d.ts
CHANGED
|
@@ -141,7 +141,7 @@ export declare const SURFACES: {
|
|
|
141
141
|
readonly rule: "charts";
|
|
142
142
|
readonly skill: readonly ["basalt-charts"];
|
|
143
143
|
readonly guardKinds: readonly ["raw-hex", "raw-color-fn", "raw-visx-axis", "unframed-chart"];
|
|
144
|
-
readonly description: "visx chart primitives, sparklines, hooks, and token re-exports (Mantine-free)";
|
|
144
|
+
readonly description: "CartesianChart + visx chart primitives, kinds, sparklines, hooks, and token re-exports (Mantine-free)";
|
|
145
145
|
readonly globs: {
|
|
146
146
|
readonly shipped: readonly ["**/charts/**"];
|
|
147
147
|
readonly repo: readonly ["packages/basalt-ui/src/charts/**"];
|
package/dist/surfaces.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"surfaces.d.ts","sourceRoot":"","sources":["../src/surfaces.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"surfaces.d.ts","sourceRoot":"","sources":["../src/surfaces.ts"],"names":[],"mappings":"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"}
|
package/dist/surfaces.js
CHANGED
|
@@ -47,7 +47,7 @@ const SURFACES = {
|
|
|
47
47
|
rule: "charts",
|
|
48
48
|
skill: ["basalt-charts"],
|
|
49
49
|
guardKinds: ["raw-hex", "raw-color-fn", "raw-visx-axis", "unframed-chart"],
|
|
50
|
-
description: "visx chart primitives, sparklines, hooks, and token re-exports (Mantine-free)",
|
|
50
|
+
description: "CartesianChart + visx chart primitives, kinds, sparklines, hooks, and token re-exports (Mantine-free)",
|
|
51
51
|
globs: {
|
|
52
52
|
shipped: ["**/charts/**"],
|
|
53
53
|
repo: ["packages/basalt-ui/src/charts/**"]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"chart-margin.test.d.ts","sourceRoot":"","sources":["../../src/tokens/chart-margin.test.ts"],"names":[],"mappings":""}
|
package/dist/tokens/index.d.ts
CHANGED
|
@@ -114,6 +114,35 @@ export declare const VX: {
|
|
|
114
114
|
};
|
|
115
115
|
readonly minPxPerTick: 55;
|
|
116
116
|
};
|
|
117
|
+
/** Resolved chart plot-area margins — the return shape of {@link chartMargin}. */
|
|
118
|
+
export type ChartMargin = {
|
|
119
|
+
top: number;
|
|
120
|
+
right: number;
|
|
121
|
+
bottom: number;
|
|
122
|
+
left: number;
|
|
123
|
+
};
|
|
124
|
+
/**
|
|
125
|
+
* Chart plot-area margins — `VX.margin` widened for an `AxisRightNumeric`'s tick labels, and/or
|
|
126
|
+
* overridden per side. A token can't express "only when a right axis is rendered", so this is a
|
|
127
|
+
* function, not a second static token — also the seam that could one day track `density`
|
|
128
|
+
* (`VX.margin` itself is frozen at the level-0 snapshot; see `deriveSpacing`'s JSDoc).
|
|
129
|
+
*
|
|
130
|
+
* Law, applied in this order: start from `VX.margin`; when `rightAxis` is true, widen `right` to
|
|
131
|
+
* `Math.max(VX.margin.right, <right-axis minimum>)` (`SPACE_STEP.chartMarginRightAxis`); then any
|
|
132
|
+
* explicitly passed side wins last (including overriding `rightAxis`'s widened `right`).
|
|
133
|
+
* `chartMargin()` with no args deep-equals `VX.margin`.
|
|
134
|
+
*
|
|
135
|
+
* Returns a NEW object every call — memoize at the call site (`useMemo`), the same way `Bars`
|
|
136
|
+
* already memoizes its own margin.
|
|
137
|
+
*/
|
|
138
|
+
export declare function chartMargin(opts?: {
|
|
139
|
+
/** Reserve room for an `AxisRightNumeric`'s tick labels. */
|
|
140
|
+
rightAxis?: boolean;
|
|
141
|
+
top?: number;
|
|
142
|
+
right?: number;
|
|
143
|
+
bottom?: number;
|
|
144
|
+
left?: number;
|
|
145
|
+
}): ChartMargin;
|
|
117
146
|
/**
|
|
118
147
|
* A px number expressed as the plain rem STRING literal (16px base: `pxRem(6)` → `'0.375rem'`) —
|
|
119
148
|
* NOT Mantine's own `rem()` helper, which instead emits a `calc(...)` expression bound to
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tokens/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAYH,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAMvE,OAAO,EAAE,EAAE,EAAE,CAAC,EAAE,MAAM,WAAW,CAAA;AAOjC,OAAO,EAAE,qBAAqB,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,UAAU,CAAA;AACnF,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAM5D,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AACxC,YAAY,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AAO7C,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAA;AACzC,YAAY,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAE5C,wFAAwF;AACxF,MAAM,MAAM,SAAS,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAA;AAEvD,8FAA8F;AAC9F,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAA;AAEjD;;;;;;GAMG;AACH,eAAO,MAAM,KAAK,GAAI,OAAO,MAAM,EAAE,GAAG,MAAM,KAAG,MACoB,CAAA;AAsCrE,eAAO,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyIL,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/tokens/index.ts"],"names":[],"mappings":"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"}
|
package/dist/tokens/index.js
CHANGED
|
@@ -158,6 +158,15 @@ const VX = {
|
|
|
158
158
|
},
|
|
159
159
|
minPxPerTick: 55
|
|
160
160
|
};
|
|
161
|
+
function chartMargin(opts) {
|
|
162
|
+
const widenedRight = opts?.rightAxis ? Math.max(VX.margin.right, SPACE_STEP.chartMarginRightAxis) : VX.margin.right;
|
|
163
|
+
return {
|
|
164
|
+
top: opts?.top ?? VX.margin.top,
|
|
165
|
+
right: opts?.right ?? widenedRight,
|
|
166
|
+
bottom: opts?.bottom ?? VX.margin.bottom,
|
|
167
|
+
left: opts?.left ?? VX.margin.left
|
|
168
|
+
};
|
|
169
|
+
}
|
|
161
170
|
const decl = (name, value) => ` --vx-${name}: ${value};`;
|
|
162
171
|
function pxRem(px) {
|
|
163
172
|
return `${px / 16}rem`;
|
|
@@ -553,6 +562,7 @@ export {
|
|
|
553
562
|
buildFontsCss,
|
|
554
563
|
buildPaletteCss,
|
|
555
564
|
buildRadiusCss,
|
|
565
|
+
chartMargin,
|
|
556
566
|
defineSeries,
|
|
557
567
|
deriveRadius,
|
|
558
568
|
deriveSpacing,
|
package/dist/tokens/palette.d.ts
CHANGED
|
@@ -501,6 +501,9 @@ declare const SPACE_STEP_BASE: {
|
|
|
501
501
|
readonly chartMarginTop: 12;
|
|
502
502
|
/** Chart plot-area margin, right (`VX.margin.right`). */
|
|
503
503
|
readonly chartMarginRight: 16;
|
|
504
|
+
/** Minimum right margin when an `AxisRightNumeric` is rendered — labels like "100"/"60m" need
|
|
505
|
+
* more room than the no-right-axis default (`chartMargin({ rightAxis: true })`, `tokens/index.ts`). */
|
|
506
|
+
readonly chartMarginRightAxis: 40;
|
|
504
507
|
/** Chart plot-area margin, bottom (`VX.margin.bottom`). */
|
|
505
508
|
readonly chartMarginBottom: 30;
|
|
506
509
|
/** Chart plot-area margin, left (`VX.margin.left`). */
|
|
@@ -870,6 +873,7 @@ export declare const SPACE_STEP: {
|
|
|
870
873
|
readonly chartLegendGap: number;
|
|
871
874
|
readonly chartMarginTop: number;
|
|
872
875
|
readonly chartMarginRight: number;
|
|
876
|
+
readonly chartMarginRightAxis: number;
|
|
873
877
|
readonly chartMarginBottom: number;
|
|
874
878
|
readonly chartMarginLeft: number;
|
|
875
879
|
readonly chartDotR: number;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"palette.d.ts","sourceRoot":"","sources":["../../src/tokens/palette.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAA;AAC5C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAA;AAExC;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;CAgCL,CAAA;AAEV;;;;GAIG;AACH,eAAO,MAAM,CAAC,GAAI,KAAK,SAAS,MAAM,EAAE,EAAE,cAAS,EAAE,aAAQ,KAAG,SAG9D,CAAA;AAEF;;;;;GAKG;AACH,eAAO,MAAM,MAAM;;;;;;;;;IASjB;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;;;;;;;;;CAaK,CAAA;AAEV;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,IAAI,EAAE,MAAM,CAAA;CACb,CAAA;AAOD;;;8DAG8D;AAC9D,eAAO,MAAM,kBAAkB;;;CAA+B,CAAA;AAE9D;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,YAAY,CAmBxD;AAMD;;;;;;;GAOG;AACH,eAAO,MAAM,MAAM;IACjB,oFAAoF;;IAEpF;2BACuB;;CAEf,CAAA;AAEV;;;;;;;GAOG;AACH,eAAO,MAAM,WAAW;IACtB;gCAC4B;;IAE5B,0FAA0F;;IAE1F,4FAA4F;;IAE5F,4EAA4E;;IAE5E,4EAA4E;;IAE5E,4EAA4E;;IAE5E;uFACmF;;CAE3E,CAAA;AAWV;;;;;;;;;;;;;;;GAeG;AACH,QAAA,MAAM,kBAAkB;IACtB,oEAAoE;;IAEpE,kEAAkE;;IAElE;yFACqF;;IAErF,kGAAkG;;IAElG;;qEAEiE;;IAEjE,gEAAgE;;IAEhE,oEAAoE;;IAEpE,gFAAgF;;IAEhF;;;mGAG+F;;CAEvF,CAAA;AAEV;;;;;;;;;;;;;;GAcG;AACH,QAAA,MAAM,gBAAgB;;;;;;CAMZ,CAAA;AAEV;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,QAAA,MAAM,eAAe;IACnB,0CAA0C;;IAY1C;;;iDAG6C;;IAE7C;;sFAEkF;;IAIlF,4CAA4C;;IAE5C,4CAA4C;;IAE5C,uCAAuC;;IAEvC,yCAAyC;;IAEzC,iDAAiD;;IAEjD,mDAAmD;;IAEnD,4DAA4D;;IAE5D,iCAAiC;;IAEjC,mCAAmC;;IAEnC,6EAA6E;;IAE7E,gCAAgC;;IAEhC,8CAA8C;;IAE9C,gDAAgD;;IAEhD,uCAAuC;;IAEvC,0CAA0C;;IAE1C,+CAA+C;;IAE/C,uCAAuC;;IAEvC,0CAA0C;;IAE1C,mCAAmC;;IAEnC,uCAAuC;;IAEvC,0CAA0C;;IAE1C;iEAC6D;;IAE7D,kGAAkG;;IAElG,kDAAkD;;IAElD,+EAA+E;;IAI/E;;;;;;;qEAOiE;;IAEjE;;;;mEAI+D;;IAE/D,wEAAwE;;IAExE,wDAAwD;;IAExD,uDAAuD;;IAEvD,iCAAiC;;IAEjC,qCAAqC;;IAErC,yDAAyD;;IAEzD,0DAA0D;;IAE1D,6CAA6C;;IAI7C,sCAAsC;;IAEtC,+BAA+B;;IAE/B,sEAAsE;;IAEtE;8EAC0E;;IAE1E,8CAA8C;;IAE9C,6BAA6B;;IAE7B,0FAA0F;;IAI1F,4BAA4B;;IAE5B,8BAA8B;;IAE9B,oCAAoC;;IAIpC,sDAAsD;;IAEtD,iCAAiC;;IAEjC,kDAAkD;;IAElD,gDAAgD;;IAIhD,yBAAyB;;IAEzB,iCAAiC;;IAEjC,mCAAmC;;IAEnC,2BAA2B;;IAI3B,oDAAoD;;IAEpD,+CAA+C;;IAE/C,8CAA8C;;IAI9C,mCAAmC;;IAEnC;;yGAEqG;;IAIrG,4BAA4B;;IAE5B,gDAAgD;;IAIhD,6CAA6C;;IAI7C,6BAA6B;;IAE7B,oCAAoC;;IAEpC,gCAAgC;;IAEhC,+BAA+B;;IAE/B,sCAAsC;;IAEtC,kCAAkC;;IAElC,sEAAsE;;IAEtE,iEAAiE;;IAEjE,mDAAmD;;IAEnD,sDAAsD;;IAEtD,oDAAoD;;IAEpD,0CAA0C;;IAE1C,mEAAmE;;IAYnE,mEAAmE;;IAEnE,4DAA4D;;IAI5D,uDAAuD;;IAIvD;;;+FAG2F;;IAE3F;;;;;;;;;;;;;;;qGAeiG;;IAEjG;sFACkF;;IAElF;iEAC6D;;IAI7D,+BAA+B;;IAO/B,uDAAuD;;IAEvD,qDAAqD;;IAErD,yDAAyD;;IAEzD,2DAA2D;;IAE3D,uDAAuD;;IAEvD;;+FAE2F;;IAI3F;;6DAEyD;;IAKzD,qGAAqG;;IAErG,wFAAwF;;IAExF,oDAAoD;;IAEpD,6DAA6D;;IAE7D,+DAA+D;;IAE/D,qEAAqE;;IAErE,uEAAuE;;IAEvE,yEAAyE;;IAEzE,kGAAkG;;IAElG,wGAAwG;;IAExG,0GAA0G;;IAE1G,oEAAoE;;IAEpE,6DAA6D;;IAE7D,+DAA+D;;CAEvD,CAAA;AAEV;;;;GAIG;AACH,eAAO,MAAM,WAAW;IACtB,oEAAoE;;IAEpE;;sEAEkE;;IAElE;;;;;;;;;iDAS6C;;CAErC,CAAA;AAEV;;;;;;;GAOG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,OAAO,EAAE;SAAG,CAAC,IAAI,MAAM,OAAO,kBAAkB,GAAG,MAAM;KAAE,CAAA;IAC3D,KAAK,EAAE;SAAG,CAAC,IAAI,MAAM,OAAO,gBAAgB,GAAG,MAAM;KAAE,CAAA;IACvD;;;;;;;gGAO4F;IAC5F,IAAI,EAAE;SAAG,CAAC,IAAI,MAAM,OAAO,eAAe,GAAG,MAAM;KAAE,GAAG;QACtD,qBAAqB,EAAE,MAAM,CAAA;QAC7B,2BAA2B,EAAE,MAAM,CAAA;KACpC,CAAA;IACD;kEAC8D;IAC9D,aAAa,EAAE,MAAM,CAAA;CACtB,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAInF;AAiCD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+GG;AACH;;;;wEAIwE;AACxE,eAAO,MAAM,mBAAmB;;;CAA+B,CAAA;AAE/D,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG,WAAW,CA2CxD;AAED;;;;;oFAKoF;AACpF,eAAO,MAAM,oBAAoB,aAAmB,CAAA;AAEpD;;2DAE2D;AAC3D,eAAO,MAAM,KAAK;;;;;;;;;;CAA+B,CAAA;AAEjD;;+BAE+B;AAC/B,eAAO,MAAM,WAAW;;;;;;CAA6B,CAAA;AAErD;;;;sGAIsG;AACtG,eAAO,MAAM,UAAU
|
|
1
|
+
{"version":3,"file":"palette.d.ts","sourceRoot":"","sources":["../../src/tokens/palette.ts"],"names":[],"mappings":"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"}
|
package/dist/tokens/palette.js
CHANGED
|
@@ -420,6 +420,9 @@ const SPACE_STEP_BASE = {
|
|
|
420
420
|
chartMarginTop: 12,
|
|
421
421
|
/** Chart plot-area margin, right (`VX.margin.right`). */
|
|
422
422
|
chartMarginRight: 16,
|
|
423
|
+
/** Minimum right margin when an `AxisRightNumeric` is rendered — labels like "100"/"60m" need
|
|
424
|
+
* more room than the no-right-axis default (`chartMargin({ rightAxis: true })`, `tokens/index.ts`). */
|
|
425
|
+
chartMarginRightAxis: 40,
|
|
423
426
|
/** Chart plot-area margin, bottom (`VX.margin.bottom`). */
|
|
424
427
|
chartMarginBottom: 30,
|
|
425
428
|
/** Chart plot-area margin, left (`VX.margin.left`). */
|
package/llms.txt
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# basalt-ui v1.
|
|
1
|
+
# basalt-ui v1.15.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.15.0
|
|
23
23
|
|
|
24
24
|
## basalt-ui
|
|
25
25
|
Import: import { ... } from 'basalt-ui'
|
|
@@ -30,7 +30,7 @@ OptionalPeers: react-markdown@^10.1.0, remark-gfm@^4.0.1, rehype-sanitize@^6.0.0
|
|
|
30
30
|
|
|
31
31
|
## basalt-ui/charts
|
|
32
32
|
Import: import { ... } from 'basalt-ui/charts'
|
|
33
|
-
Description: visx chart primitives, sparklines, hooks, and token re-exports (Mantine-free)
|
|
33
|
+
Description: CartesianChart + visx chart primitives, kinds, sparklines, hooks, and token re-exports (Mantine-free)
|
|
34
34
|
Layer: headless
|
|
35
35
|
Rule: basalt-charts
|
|
36
36
|
|
package/package.json
CHANGED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test'
|
|
2
|
+
import { buildDomainIndex, parseKey, resolveCursorPoint } from './resolve'
|
|
3
|
+
|
|
4
|
+
type Point = { date: string }
|
|
5
|
+
|
|
6
|
+
const daily = (days: string[]): Point[] => days.map((d) => ({ date: `2026-08-${d}` }))
|
|
7
|
+
const index = (points: Point[]) => buildDomainIndex(points, (p) => p.date)
|
|
8
|
+
|
|
9
|
+
describe('parseKey', () => {
|
|
10
|
+
test('reads numeric strings as numbers', () => {
|
|
11
|
+
expect(parseKey('42')).toBe(42)
|
|
12
|
+
expect(parseKey('-1.5')).toBe(-1.5)
|
|
13
|
+
})
|
|
14
|
+
|
|
15
|
+
test('reads ISO dates as timestamps', () => {
|
|
16
|
+
expect(parseKey('2026-08-18')).toBe(Date.parse('2026-08-18'))
|
|
17
|
+
})
|
|
18
|
+
|
|
19
|
+
test('returns null for a plain category', () => {
|
|
20
|
+
expect(parseKey('Direct')).toBeNull()
|
|
21
|
+
})
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
describe('resolveCursorPoint', () => {
|
|
25
|
+
test('resolves an exact key', () => {
|
|
26
|
+
const i = index(daily(['01', '02', '03']))
|
|
27
|
+
expect(resolveCursorPoint(i, '2026-08-02')?.date).toBe('2026-08-02')
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
test('resolves a FOLDED sibling key to the nearest own point', () => {
|
|
31
|
+
// The failure this replaced: a chart holding every other day never owned the keys its unfolded sibling
|
|
32
|
+
// broadcast, so the shared crosshair appeared on roughly one hover in two.
|
|
33
|
+
const folded = index(daily(['01', '03', '05']))
|
|
34
|
+
expect(resolveCursorPoint(folded, '2026-08-02')?.date).toBe('2026-08-01')
|
|
35
|
+
expect(resolveCursorPoint(folded, '2026-08-04')?.date).toBe('2026-08-03')
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
test('returns null beyond one domain step, so unrelated charts never sync', () => {
|
|
39
|
+
const i = index(daily(['01', '02', '03']))
|
|
40
|
+
expect(resolveCursorPoint(i, '2026-09-30')).toBeNull()
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
test('tolerates one step past each end', () => {
|
|
44
|
+
const i = index(daily(['02', '03', '04']))
|
|
45
|
+
expect(resolveCursorPoint(i, '2026-08-01')?.date).toBe('2026-08-02')
|
|
46
|
+
expect(resolveCursorPoint(i, '2026-08-05')?.date).toBe('2026-08-04')
|
|
47
|
+
})
|
|
48
|
+
|
|
49
|
+
test('categorical domains fall back to exact match only', () => {
|
|
50
|
+
const points = [{ date: 'Direct' }, { date: 'Referral' }]
|
|
51
|
+
const i = buildDomainIndex(points, (p) => p.date)
|
|
52
|
+
expect(resolveCursorPoint(i, 'Direct')?.date).toBe('Direct')
|
|
53
|
+
expect(resolveCursorPoint(i, 'Organic')).toBeNull()
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
test('a single-point chart resolves only its own key', () => {
|
|
57
|
+
const i = index(daily(['01']))
|
|
58
|
+
expect(resolveCursorPoint(i, '2026-08-01')?.date).toBe('2026-08-01')
|
|
59
|
+
expect(resolveCursorPoint(i, '2026-08-02')).toBeNull()
|
|
60
|
+
})
|
|
61
|
+
})
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Domain-aware resolution of a BROADCAST cursor key against a chart's own points.
|
|
3
|
+
*
|
|
4
|
+
* The layer this replaced matched keys byte-for-byte, so two charts shared a cursor only if they emitted identical
|
|
5
|
+
* strings. A chart that folded its domain to fit a narrow viewport stopped owning most of the keys
|
|
6
|
+
* its siblings broadcast, and the shared crosshair then landed on roughly one hover in three with
|
|
7
|
+
* no rule a reader could infer — patched per chart by a manual `resolveKey`. Resolving on the
|
|
8
|
+
* PARSED domain instead makes that automatic (`docs/CHARTS-SPEC.md` §3).
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* A key as a comparable number: numeric-looking strings as numbers, everything else as a date.
|
|
13
|
+
* Neither (a plain category like "Direct") → null, which degrades to exact-match only — correct,
|
|
14
|
+
* since "nearest category" is meaningless.
|
|
15
|
+
*/
|
|
16
|
+
export function parseKey(key: string): number | null {
|
|
17
|
+
if (/^[-+]?\d*\.?\d+$/.test(key)) {
|
|
18
|
+
const n = Number(key)
|
|
19
|
+
return Number.isFinite(n) ? n : null
|
|
20
|
+
}
|
|
21
|
+
const t = Date.parse(key)
|
|
22
|
+
return Number.isNaN(t) ? null : t
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export type DomainIndex<T> = {
|
|
26
|
+
byKey: Map<string, T>
|
|
27
|
+
/** Parsed positions, ascending. Empty when the domain isn't parseable. */
|
|
28
|
+
values: number[]
|
|
29
|
+
/** Points parallel to `values`. */
|
|
30
|
+
points: T[]
|
|
31
|
+
/** Median spacing — the tolerance within which a foreign key still resolves. */
|
|
32
|
+
step: number
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** Build the lookup structure once per `data` identity. */
|
|
36
|
+
export function buildDomainIndex<T>(data: readonly T[], getKey: (d: T) => string): DomainIndex<T> {
|
|
37
|
+
const byKey = new Map<string, T>()
|
|
38
|
+
const parsed: { value: number; point: T }[] = []
|
|
39
|
+
let parseable = true
|
|
40
|
+
|
|
41
|
+
for (const d of data) {
|
|
42
|
+
byKey.set(getKey(d), d)
|
|
43
|
+
if (!parseable) continue
|
|
44
|
+
const value = parseKey(getKey(d))
|
|
45
|
+
if (value === null) parseable = false
|
|
46
|
+
else parsed.push({ value, point: d })
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Under two parseable points there is no step to reason about, so nearest-match would be
|
|
50
|
+
// unbounded — fall back to exact match.
|
|
51
|
+
if (!parseable || parsed.length < 2) return { byKey, values: [], points: [], step: 0 }
|
|
52
|
+
|
|
53
|
+
parsed.sort((a, b) => a.value - b.value)
|
|
54
|
+
const values = parsed.map((p) => p.value)
|
|
55
|
+
const points = parsed.map((p) => p.point)
|
|
56
|
+
|
|
57
|
+
const diffs: number[] = []
|
|
58
|
+
for (let i = 1; i < values.length; i++) {
|
|
59
|
+
diffs.push((values[i] as number) - (values[i - 1] as number))
|
|
60
|
+
}
|
|
61
|
+
diffs.sort((a, b) => a - b)
|
|
62
|
+
|
|
63
|
+
return { byKey, values, points, step: diffs[Math.floor(diffs.length / 2)] ?? 0 }
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* The own point a broadcast `key` maps to: exact match first, else the nearest parsed position
|
|
68
|
+
* within one domain step. Out of range → null, so charts over unrelated domains never paint a
|
|
69
|
+
* cursor for each other — which is what makes "shared by default" safe.
|
|
70
|
+
*/
|
|
71
|
+
export function resolveCursorPoint<T>(index: DomainIndex<T>, key: string): T | null {
|
|
72
|
+
const exact = index.byKey.get(key)
|
|
73
|
+
if (exact !== undefined) return exact
|
|
74
|
+
|
|
75
|
+
const target = parseKey(key)
|
|
76
|
+
if (target === null || index.values.length === 0) return null
|
|
77
|
+
|
|
78
|
+
const first = index.values[0] as number
|
|
79
|
+
const last = index.values[index.values.length - 1] as number
|
|
80
|
+
if (target < first - index.step || target > last + index.step) return null
|
|
81
|
+
|
|
82
|
+
let lo = 0
|
|
83
|
+
let hi = index.values.length - 1
|
|
84
|
+
while (lo < hi) {
|
|
85
|
+
const mid = (lo + hi) >> 1
|
|
86
|
+
if ((index.values[mid] as number) < target) lo = mid + 1
|
|
87
|
+
else hi = mid
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
let best = lo
|
|
91
|
+
let bestDist = Math.abs((index.values[lo] as number) - target)
|
|
92
|
+
if (lo > 0) {
|
|
93
|
+
const prevDist = Math.abs((index.values[lo - 1] as number) - target)
|
|
94
|
+
// `<=`, not `<`: a key landing exactly between two own points belongs to the EARLIER one —
|
|
95
|
+
// the folded bucket that swallowed it starts there, so ties resolve backwards, consistently.
|
|
96
|
+
if (prevDist <= bestDist) {
|
|
97
|
+
best = lo - 1
|
|
98
|
+
bestDist = prevDist
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return bestDist <= index.step ? (index.points[best] as T) : null
|
|
102
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { createContext, useContext, useMemo, useSyncExternalStore } from 'react'
|
|
2
|
+
import type { ReactNode } from 'react'
|
|
3
|
+
import type { CursorState, CursorStore } from './store'
|
|
4
|
+
import { createCursorStore, EMPTY_CURSOR, globalCursorStore } from './store'
|
|
5
|
+
|
|
6
|
+
const CursorStoreContext = createContext<CursorStore>(globalCursorStore)
|
|
7
|
+
|
|
8
|
+
export type ChartCursorScopeProps = {
|
|
9
|
+
children: ReactNode
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* ISOLATES its subtree onto a private cursor store — the inverse of the removed `ChartHoverSync`, which
|
|
14
|
+
* had to be mounted to enable sharing at all. Charts share a cursor by default now; wrap a group
|
|
15
|
+
* in this only when it must NOT follow the rest of the page (e.g. two independent dashboards
|
|
16
|
+
* rendered side by side over the same calendar).
|
|
17
|
+
*/
|
|
18
|
+
export function ChartCursorScope({ children }: ChartCursorScopeProps) {
|
|
19
|
+
const store = useMemo(() => createCursorStore(), [])
|
|
20
|
+
return <CursorStoreContext.Provider value={store}>{children}</CursorStoreContext.Provider>
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** The cursor store this chart belongs to — the nearest `ChartCursorScope`, else the global one. */
|
|
24
|
+
export function useCursorStore(): CursorStore {
|
|
25
|
+
return useContext(CursorStoreContext)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** Subscribe to the ambient cursor. Re-renders only on an actual cursor change. */
|
|
29
|
+
export function useCursorState(): CursorState {
|
|
30
|
+
const store = useCursorStore()
|
|
31
|
+
return useSyncExternalStore(store.subscribe, store.get, () => EMPTY_CURSOR)
|
|
32
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { describe, expect, test } from 'bun:test'
|
|
2
|
+
import { createCursorStore, globalCursorStore } from './store'
|
|
3
|
+
|
|
4
|
+
describe('cursor store', () => {
|
|
5
|
+
test('notifies subscribers on change', () => {
|
|
6
|
+
const store = createCursorStore()
|
|
7
|
+
let calls = 0
|
|
8
|
+
const unsubscribe = store.subscribe(() => {
|
|
9
|
+
calls++
|
|
10
|
+
})
|
|
11
|
+
|
|
12
|
+
store.set('2026-08-18', 'chart-a')
|
|
13
|
+
expect(calls).toBe(1)
|
|
14
|
+
expect(store.get()).toEqual({ key: '2026-08-18', source: 'chart-a' })
|
|
15
|
+
|
|
16
|
+
unsubscribe()
|
|
17
|
+
store.set('2026-08-19', 'chart-a')
|
|
18
|
+
expect(calls).toBe(1)
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
test('a redundant set does not notify — a re-hover of the same point costs no render', () => {
|
|
22
|
+
const store = createCursorStore()
|
|
23
|
+
let calls = 0
|
|
24
|
+
store.subscribe(() => {
|
|
25
|
+
calls++
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
store.set('2026-08-18', 'chart-a')
|
|
29
|
+
store.set('2026-08-18', 'chart-a')
|
|
30
|
+
expect(calls).toBe(1)
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
test('clearing returns the stable empty identity', () => {
|
|
34
|
+
const store = createCursorStore()
|
|
35
|
+
const before = store.get()
|
|
36
|
+
store.set('2026-08-18', 'chart-a')
|
|
37
|
+
store.set(null, null)
|
|
38
|
+
expect(store.get()).toBe(before)
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
test('scoped stores are independent of the global one', () => {
|
|
42
|
+
const scoped = createCursorStore()
|
|
43
|
+
scoped.set('2026-08-18', 'chart-a')
|
|
44
|
+
expect(globalCursorStore.get().key).toBeNull()
|
|
45
|
+
})
|
|
46
|
+
})
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shared chart cursor — an external store, not a React context value.
|
|
3
|
+
*
|
|
4
|
+
* Sharing used to require a `ChartHoverSync` ancestor, so it was opt-in and silently absent
|
|
5
|
+
* whenever someone forgot the wrapper. This inverts that: a module-level store means every
|
|
6
|
+
* chart on the page shares a cursor by default, and `ChartCursorScope` exists to ISOLATE a
|
|
7
|
+
* subtree (`docs/CHARTS-SPEC.md` §3). Charts with non-overlapping domains never resolve each
|
|
8
|
+
* other's keys, so "shared by default" cannot produce a false cursor.
|
|
9
|
+
*
|
|
10
|
+
* External store rather than context for a second reason: a cursor move re-renders only the
|
|
11
|
+
* charts subscribed to it, instead of every descendant of a provider whose value object is new
|
|
12
|
+
* on each pointer frame.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/** Broadcast cursor position: an opaque domain key plus the chart that owns it. */
|
|
16
|
+
export type CursorState = {
|
|
17
|
+
/** The hovered x-domain key (a date/category string), or null when nothing is hovered. */
|
|
18
|
+
key: string | null
|
|
19
|
+
/** `chartId` of the chart the pointer is actually over — the one that owns the tooltip. */
|
|
20
|
+
source: string | null
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type CursorStore = {
|
|
24
|
+
get: () => CursorState
|
|
25
|
+
set: (key: string | null, source: string | null) => void
|
|
26
|
+
subscribe: (onChange: () => void) => () => void
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Stable identity for "nothing hovered" — also the SSR snapshot, so it must never be recreated. */
|
|
30
|
+
export const EMPTY_CURSOR: CursorState = { key: null, source: null }
|
|
31
|
+
|
|
32
|
+
export function createCursorStore(): CursorStore {
|
|
33
|
+
let state: CursorState = EMPTY_CURSOR
|
|
34
|
+
const listeners = new Set<() => void>()
|
|
35
|
+
|
|
36
|
+
return {
|
|
37
|
+
get: () => state,
|
|
38
|
+
set: (key, source) => {
|
|
39
|
+
if (state.key === key && state.source === source) return
|
|
40
|
+
state = key === null && source === null ? EMPTY_CURSOR : { key, source }
|
|
41
|
+
for (const listener of listeners) listener()
|
|
42
|
+
},
|
|
43
|
+
subscribe: (onChange) => {
|
|
44
|
+
listeners.add(onChange)
|
|
45
|
+
return () => {
|
|
46
|
+
listeners.delete(onChange)
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** The default store every chart shares unless a `ChartCursorScope` overrides it. */
|
|
53
|
+
export const globalCursorStore: CursorStore = createCursorStore()
|