basalt-ui 1.31.0 → 1.32.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 +23 -0
- package/MIGRATING.md +101 -17
- package/agent/rules/basalt-mantine.md +4 -4
- package/configs/oxlint-plugin.js +159 -42
- package/configs/oxlint-plugin.test.ts +182 -0
- package/dist/charts/kinds/BandStrip.d.ts.map +1 -1
- package/dist/charts/kinds/BandStrip.js +18 -66
- package/dist/charts/kinds/DualPanel.d.ts.map +1 -1
- package/dist/charts/kinds/DualPanel.js +5 -26
- package/dist/charts/kinds/MirroredBars.d.ts.map +1 -1
- package/dist/charts/kinds/MirroredBars.js +10 -68
- package/dist/charts/kinds/band-chrome.d.ts +31 -0
- package/dist/charts/kinds/band-chrome.d.ts.map +1 -0
- package/dist/charts/kinds/band-chrome.js +71 -0
- package/dist/charts/primitives/CartesianChart.d.ts.map +1 -1
- package/dist/charts/primitives/CartesianChart.js +49 -76
- package/dist/charts/primitives/ChartCard.d.ts.map +1 -1
- package/dist/charts/primitives/ChartCard.js +5 -23
- package/dist/charts/primitives/ChartFrame.d.ts.map +1 -1
- package/dist/charts/primitives/ChartFrame.js +23 -40
- package/dist/charts/primitives/ChartLegend.d.ts.map +1 -1
- package/dist/charts/primitives/ChartLegend.js +5 -133
- package/dist/charts/primitives/ChartTooltip.d.ts +5 -0
- package/dist/charts/primitives/ChartTooltip.d.ts.map +1 -1
- package/dist/charts/primitives/ChartTooltip.js +16 -1
- package/dist/charts/primitives/HoverOverlay.d.ts +24 -9
- package/dist/charts/primitives/HoverOverlay.d.ts.map +1 -1
- package/dist/charts/primitives/HoverOverlay.js +17 -1
- package/dist/charts/primitives/chart-card-context.d.ts +17 -13
- package/dist/charts/primitives/chart-card-context.d.ts.map +1 -1
- package/dist/charts/primitives/chart-card-context.js +10 -5
- package/dist/charts/primitives/chart-frame-layout.d.ts.map +1 -1
- package/dist/charts/primitives/chart-frame-layout.js +25 -31
- package/dist/charts/primitives/legend-disclosure.d.ts +27 -0
- package/dist/charts/primitives/legend-disclosure.d.ts.map +1 -0
- package/dist/charts/primitives/legend-disclosure.js +153 -0
- package/dist/controls/controls.module.css +5 -0
- package/dist/controls/sync-button.module.css +1 -0
- package/dist/dashboard/stat-group.module.css +19 -64
- package/dist/dashboard/widget-grid.module.css +11 -38
- package/dist/data/data-table-state.d.ts +83 -0
- package/dist/data/data-table-state.d.ts.map +1 -0
- package/dist/data/data-table-state.js +139 -0
- package/dist/data/data-table.d.ts +33 -2
- package/dist/data/data-table.d.ts.map +1 -1
- package/dist/data/data-table.js +179 -277
- package/dist/forms/form-layout.module.css +3 -13
- package/dist/guard/index.d.ts.map +1 -1
- package/dist/guard/index.js +6 -5
- package/dist/notifications/center.d.ts.map +1 -1
- package/dist/provider/index.d.ts.map +1 -1
- package/dist/provider/index.js +12 -10
- package/dist/shell/aside-docking.d.ts +31 -0
- package/dist/shell/aside-docking.d.ts.map +1 -0
- package/dist/shell/aside-docking.js +19 -0
- package/dist/shell/index.d.ts +0 -11
- package/dist/shell/index.d.ts.map +1 -1
- package/dist/shell/index.js +13 -14
- package/dist/shell/use-breakpoint.d.ts +1 -1
- package/dist/theme/floating.module.css +1 -0
- package/dist/theme-lab/derive-controls.d.ts.map +1 -1
- package/llms.txt +2 -2
- package/package.json +2 -1
- package/src/charts/kinds/BandStrip.tsx +18 -64
- package/src/charts/kinds/DualPanel.tsx +4 -24
- package/src/charts/kinds/MirroredBars.tsx +12 -66
- package/src/charts/kinds/band-chrome.tsx +91 -0
- package/src/charts/primitives/CartesianChart.tsx +79 -122
- package/src/charts/primitives/ChartCard.tsx +5 -27
- package/src/charts/primitives/ChartFrame.tsx +37 -76
- package/src/charts/primitives/ChartLegend.tsx +7 -154
- package/src/charts/primitives/ChartTooltip.tsx +16 -0
- package/src/charts/primitives/HoverOverlay.tsx +52 -21
- package/src/charts/primitives/chart-card-context.ts +25 -16
- package/src/charts/primitives/chart-frame-layout.ts +44 -56
- package/src/charts/primitives/legend-disclosure.tsx +177 -0
- package/src/controls/controls.module.css +5 -0
- package/src/controls/sync-button.module.css +1 -0
- package/src/dashboard/stat-group.module.css +19 -64
- package/src/dashboard/widget-grid.module.css +11 -38
- package/src/data/data-table-state.ts +218 -0
- package/src/data/data-table.tsx +127 -218
- package/src/forms/form-layout.module.css +3 -13
- package/src/guard/index.ts +21 -16
- package/src/notifications/center.tsx +3 -0
- package/src/provider/index.tsx +18 -17
- package/src/shell/aside-docking.ts +54 -0
- package/src/shell/index.tsx +15 -37
- package/src/shell/use-breakpoint.ts +2 -2
- package/src/theme/floating.module.css +1 -0
- package/src/theme-lab/derive-controls.tsx +2 -0
package/dist/guard/index.js
CHANGED
|
@@ -33,7 +33,8 @@ const SIZE_CLASS_LITERALS = /* @__PURE__ */ new Set(["52.5em", "75em", "52.49375
|
|
|
33
33
|
const SHELL_CSS_PATH = /(?:^|\/)shell\//;
|
|
34
34
|
function isSanctionedShellMediaQuery(relPath, query) {
|
|
35
35
|
if (!SHELL_CSS_PATH.test(relPath)) return false;
|
|
36
|
-
const
|
|
36
|
+
const widthClauses = query.match(/\([^()]*\bwidth\b[^()]*\)/g) ?? [];
|
|
37
|
+
const widths = widthClauses.flatMap((c) => c.match(new RegExp(CSS_WIDTH_NUM, "g")) ?? []);
|
|
37
38
|
return widths.length > 0 && widths.every((w) => SIZE_CLASS_LITERALS.has(w));
|
|
38
39
|
}
|
|
39
40
|
const MICRO_RADIUS_CEILING_PX = 3;
|
|
@@ -230,13 +231,13 @@ const DEFAULT_GUARD_CONFIG = {
|
|
|
230
231
|
const GRACE_PERIOD_KINDS = {
|
|
231
232
|
"raw-selection-control": {
|
|
232
233
|
since: "1.26.0",
|
|
233
|
-
promote: "1.
|
|
234
|
-
why: 'Extended 1.31.0 -> 1.32.0 at the 1.31.0 release: the basalt tree still carries incumbents (data-table, notifications center, theme-lab) and the argo sweep has not run. new in the wave-6 control guards (docs/CONTROLS-SPEC.md \xA76, law C1). The text lane cannot see ancestry, so "no home" is approximated by a 12-line host-tag window \u2014 the loosest reading in the guard, and the reason this one lands warn rather than error even though its law is settled. ONE LAW, TWO LANES, ONE PROMOTION: it moves with its AST twin, `basalt/control-outside-home`, whose PLUGIN_RULE_GRACE entry carries the full argument and the measurement, and it must never be promoted or extended alone \u2014 a consumer waiving one spelling and failing the other is the exact confusion `WAIVER_ID_ALIASES` exists to close. RE-MEASURED 2026-09-09 across the whole fleet on 1.29.2: 29 AST-lane incumbents in five repos, plus three of basalt
|
|
234
|
+
promote: "1.33.0",
|
|
235
|
+
why: "Extended 1.32.0 -> 1.33.0 at the 1.32.0 release, the last extension: basalt's own tree is at 0 (consumer-loop r1 dogfood) and argo is at 2, but RE-MEASURED 2026-09-30 the fleet carries ~33 (image-gen 11, rb 7, image-share 6, weatherorb 5, email-gateway 4, argo 2). The 1.32.0 fleet upgrade clears them while they warn, together with raw-breakpoint, so 1.33.0 promotes all four ids against a measured-clean fleet. Extended 1.31.0 -> 1.32.0 at the 1.31.0 release: the basalt tree still carries incumbents (data-table, notifications center, theme-lab) and the argo sweep has not run. new in the wave-6 control guards (docs/CONTROLS-SPEC.md \xA76, law C1). The text lane cannot see ancestry, so \"no home\" is approximated by a 12-line host-tag window \u2014 the loosest reading in the guard, and the reason this one lands warn rather than error even though its law is settled. ONE LAW, TWO LANES, ONE PROMOTION: it moves with its AST twin, `basalt/control-outside-home`, whose PLUGIN_RULE_GRACE entry carries the full argument and the measurement, and it must never be promoted or extended alone \u2014 a consumer waiving one spelling and failing the other is the exact confusion `WAIVER_ID_ALIASES` exists to close. RE-MEASURED 2026-09-09 across the whole fleet on 1.29.2: 29 AST-lane incumbents in five repos, plus three of basalt's own (data-table, notifications/center, theme-lab). Extended to 1.31.0 because 1.30.0 is the FIRST release in which either lane names a home a provider-only consumer can reach \u2014 every home both messages listed was a shell slot, and the shell-less `PageBar` sentence ships in this minor. Promoting in the same minor that makes a rule satisfiable gives nobody a window to act. 1.31.0 is when the 29 are re-measured against that number."
|
|
235
236
|
},
|
|
236
237
|
"raw-media-query": {
|
|
237
238
|
since: "1.31.0",
|
|
238
|
-
promote: "1.
|
|
239
|
-
why: "
|
|
239
|
+
promote: "1.33.0",
|
|
240
|
+
why: "The CSS-text twin of the oxlint plugin's `basalt/raw-breakpoint` \u2014 ONE LAW, TWO LANES, ONE PROMOTION, so it moves with that rule and never alone (`configs/oxlint-plugin.test.ts` \u203A twin lanes share one promotion pins the pair). New in the wave-11 responsive/touch guards; reaching CSS is a NEW file type for this check, same reasoning as `css-raw-surface`, with incumbents in basalt's own tree at ship time, so it shipped warn. Extended 1.32.0 -> 1.33.0 with its twin: 1.31.0 named no reachable container primitive, and 1.32.0 is the first minor that documents one (`CONTAINER_CLASSES` + the declared `@container` names, MIGRATING \xA7 `basalt/raw-breakpoint`)."
|
|
240
241
|
}
|
|
241
242
|
};
|
|
242
243
|
function severityOf(kind, cfg) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"center.d.ts","sourceRoot":"","sources":["../../src/notifications/center.tsx"],"names":[],"mappings":"AA2BA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAyClD;;4CAE4C;AAC5C,eAAO,MAAM,0BAA0B,MAAM,CAAA;AAwF7C,MAAM,MAAM,uBAAuB,GAAG,WAAW,GAAG;IAClD,6DAA6D;IAC7D,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;CAC5B,CAAA;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,SAAe,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,uBAAuB,+
|
|
1
|
+
{"version":3,"file":"center.d.ts","sourceRoot":"","sources":["../../src/notifications/center.tsx"],"names":[],"mappings":"AA2BA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;AAyClD;;4CAE4C;AAC5C,eAAO,MAAM,0BAA0B,MAAM,CAAA;AAwF7C,MAAM,MAAM,uBAAuB,GAAG,WAAW,GAAG;IAClD,6DAA6D;IAC7D,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;CAC5B,CAAA;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,kBAAkB,CAAC,EAAE,SAAe,EAAE,SAAS,EAAE,KAAK,EAAE,EAAE,uBAAuB,+BA4EhG"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/provider/index.tsx"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAA;AACzD,OAAO,EAAE,SAAS,EAAsB,MAAM,OAAO,CAAA;AACrD,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAIjD,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,sBAAsB,CAAA;AAIrE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAA;AASjD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AAExD;;;;;;;;;;GAUG;AACH,MAAM,MAAM,kBAAkB,GAC1B;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,SAAS,CAAA;CAAE,GACnC;IAAE,IAAI,EAAE,QAAQ,CAAC;IAAC,KAAK,EAAE,UAAU,CAAA;CAAE,GACrC;IAAE,IAAI,EAAE,oBAAoB,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,CAAA;AAEnD,MAAM,MAAM,mBAAmB,GAAG;IAChC,QAAQ,EAAE,SAAS,CAAA;IACnB,oFAAoF;IACpF,KAAK,CAAC,EAAE,oBAAoB,CAAC,OAAO,CAAC,CAAA;IACrC;;;;;;;;;;;;;;;;;;OAkBG;IACH,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB;;;OAGG;IACH,cAAc,CAAC,EAAE,gBAAgB,CAAA;IACjC,8CAA8C;IAC9C,kBAAkB,CAAC,EAAE,oBAAoB,CAAC,oBAAoB,CAAC,CAAA;IAC/D;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,kBAAkB,KAAK,IAAI,CAAA;IAC3D;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;;;;OAKG;IACH,YAAY,CAAC,EAAE,IAAI,CAAC,yBAAyB,EAAE,UAAU,CAAC,CAAA;IAC1D;;;;;;OAMG;IACH,aAAa,CAAC,EAAE,SAAS,CAAA;CAC1B,GAAG,IAAI,CAAC,oBAAoB,EAAE,UAAU,GAAG,OAAO,GAAG,oBAAoB,GAAG,sBAAsB,CAAC,CAAA;AAcpG;;;GAGG;AACH,MAAM,MAAM,wBAAwB,GAAG;IACrC,QAAQ,EAAE,SAAS,CAAA;IACnB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,EAAE,kBAAkB,KAAK,IAAI,CAAA;IAC3D;;;;OAIG;IACH,QAAQ,CAAC,EAAE,SAAS,GAAG,CAAC,CAAC,KAAK,EAAE,OAAO,KAAK,SAAS,CAAC,CAAA;CACvD,CAAA;AAED,KAAK,aAAa,GAAG;IAAE,QAAQ,EAAE,OAAO,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,CAAA;AA2B1D;;;;;;;;;GASG;AACH,qBAAa,mBAAoB,SAAQ,SAAS,CAAC,wBAAwB,EAAE,aAAa,CAAC;gBAC7E,KAAK,EAAE,wBAAwB;IAK3C,MAAM,CAAC,wBAAwB,CAAC,KAAK,EAAE,OAAO,GAAG,aAAa;IAIrD,iBAAiB,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,GAAG,IAAI;IAKxD,MAAM,IAAI,SAAS;CAS7B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,kBAAkB,CAChC,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GACjB,MAAM,CAER;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/provider/index.tsx"],"names":[],"mappings":"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"}
|
package/dist/provider/index.js
CHANGED
|
@@ -70,17 +70,22 @@ class BasaltErrorBoundary extends Component {
|
|
|
70
70
|
function composeInjectedCss(base, fontsCss, radiusCss, densityCss) {
|
|
71
71
|
return [base, fontsCss, radiusCss, densityCss].filter(Boolean).join("\n");
|
|
72
72
|
}
|
|
73
|
-
|
|
73
|
+
function createMountCounter() {
|
|
74
|
+
let count = 0;
|
|
75
|
+
return { mount: () => ++count, unmount: () => --count };
|
|
76
|
+
}
|
|
77
|
+
const hostAttributeMounts = createMountCounter();
|
|
74
78
|
function useHostAttribute() {
|
|
75
79
|
const isPwa = useMediaQuery("(display-mode: standalone)", false);
|
|
76
80
|
useIsomorphicLayoutEffect(() => {
|
|
77
81
|
document.documentElement.setAttribute("data-basalt-host", isPwa ? "pwa" : "web");
|
|
78
82
|
}, [isPwa]);
|
|
79
83
|
useIsomorphicLayoutEffect(() => {
|
|
80
|
-
|
|
84
|
+
hostAttributeMounts.mount();
|
|
81
85
|
return () => {
|
|
82
|
-
|
|
83
|
-
|
|
86
|
+
if (hostAttributeMounts.unmount() === 0) {
|
|
87
|
+
document.documentElement.removeAttribute("data-basalt-host");
|
|
88
|
+
}
|
|
84
89
|
};
|
|
85
90
|
}, []);
|
|
86
91
|
}
|
|
@@ -134,18 +139,15 @@ function BasaltBridge({
|
|
|
134
139
|
children
|
|
135
140
|
] });
|
|
136
141
|
}
|
|
137
|
-
|
|
142
|
+
const providerMounts = createMountCounter();
|
|
138
143
|
function useDuplicateProviderGuard() {
|
|
139
144
|
useEffect(() => {
|
|
140
|
-
|
|
141
|
-
if (mountedProviderCount > 1 && isDev()) {
|
|
145
|
+
if (providerMounts.mount() > 1 && isDev()) {
|
|
142
146
|
console.warn(
|
|
143
147
|
"[basalt] BasaltProvider: more than one instance is mounted at once \u2014 nested/duplicate BasaltProviders double-mount MantineProvider, the palette <style> and ConnectivityProvider. Mount exactly one, at the app root."
|
|
144
148
|
);
|
|
145
149
|
}
|
|
146
|
-
return () =>
|
|
147
|
-
mountedProviderCount--;
|
|
148
|
-
};
|
|
150
|
+
return () => void providerMounts.unmount();
|
|
149
151
|
}, []);
|
|
150
152
|
}
|
|
151
153
|
function BasaltProvider({
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { SizeClass } from './use-size-class.js';
|
|
2
|
+
export type AsideDockingInput = {
|
|
3
|
+
claimed: boolean;
|
|
4
|
+
folded: boolean;
|
|
5
|
+
sizeClass: SizeClass;
|
|
6
|
+
/** The `asideDockQuery` media query's result. */
|
|
7
|
+
roomToDock: boolean;
|
|
8
|
+
asideWidth: number;
|
|
9
|
+
asideRailWidth: number;
|
|
10
|
+
};
|
|
11
|
+
export type AsideDocking = {
|
|
12
|
+
/** An open aside pushes main rather than overlaying it. */
|
|
13
|
+
docks: boolean;
|
|
14
|
+
/** An open aside renders over main (reserving only its rail). */
|
|
15
|
+
overlay: boolean;
|
|
16
|
+
/** The width `AppShell` reserves for the region. */
|
|
17
|
+
width: number;
|
|
18
|
+
};
|
|
19
|
+
/** The `min-width` query that holds while navbar + open aside + `MAIN_MIN_WIDTH` fit side by side. */
|
|
20
|
+
export declare function asideDockQuery(input: {
|
|
21
|
+
navbarWidth: number;
|
|
22
|
+
asideWidth: number;
|
|
23
|
+
}): string;
|
|
24
|
+
/**
|
|
25
|
+
* The room-to-dock query's SSR/no-`matchMedia` fallback — an `'expanded'` `sizeClassHint` means the
|
|
26
|
+
* server already believes there is room, matching `useSizeClass`'s own hint-seeded guard
|
|
27
|
+
* (`use-size-class.ts`), so the first client paint doesn't fold a default-open aside for one commit.
|
|
28
|
+
*/
|
|
29
|
+
export declare function roomToDockServerFallback(sizeClassHint: SizeClass | undefined): boolean;
|
|
30
|
+
export declare function resolveAsideDocking(input: AsideDockingInput): AsideDocking;
|
|
31
|
+
//# sourceMappingURL=aside-docking.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aside-docking.d.ts","sourceRoot":"","sources":["../../src/shell/aside-docking.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAQjD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,OAAO,EAAE,OAAO,CAAA;IAChB,MAAM,EAAE,OAAO,CAAA;IACf,SAAS,EAAE,SAAS,CAAA;IACpB,iDAAiD;IACjD,UAAU,EAAE,OAAO,CAAA;IACnB,UAAU,EAAE,MAAM,CAAA;IAClB,cAAc,EAAE,MAAM,CAAA;CACvB,CAAA;AAED,MAAM,MAAM,YAAY,GAAG;IACzB,2DAA2D;IAC3D,KAAK,EAAE,OAAO,CAAA;IACd,iEAAiE;IACjE,OAAO,EAAE,OAAO,CAAA;IAChB,oDAAoD;IACpD,KAAK,EAAE,MAAM,CAAA;CACd,CAAA;AAED,sGAAsG;AACtG,wBAAgB,cAAc,CAAC,KAAK,EAAE;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAEzF;AAED;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,aAAa,EAAE,SAAS,GAAG,SAAS,GAAG,OAAO,CAEtF;AAED,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,iBAAiB,GAAG,YAAY,CAK1E"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { toEm } from "../tokens/size-classes.js";
|
|
2
|
+
const MAIN_MIN_WIDTH = 720;
|
|
3
|
+
function asideDockQuery(input) {
|
|
4
|
+
return `(min-width: ${toEm(input.navbarWidth + input.asideWidth + MAIN_MIN_WIDTH)})`;
|
|
5
|
+
}
|
|
6
|
+
function roomToDockServerFallback(sizeClassHint) {
|
|
7
|
+
return sizeClassHint === "expanded";
|
|
8
|
+
}
|
|
9
|
+
function resolveAsideDocking(input) {
|
|
10
|
+
const docks = input.sizeClass === "expanded" && input.roomToDock;
|
|
11
|
+
const open = input.claimed && !input.folded;
|
|
12
|
+
const reserved = open && docks ? input.asideWidth : input.asideRailWidth;
|
|
13
|
+
return { docks, overlay: open && !docks, width: input.claimed ? reserved : 0 };
|
|
14
|
+
}
|
|
15
|
+
export {
|
|
16
|
+
asideDockQuery,
|
|
17
|
+
resolveAsideDocking,
|
|
18
|
+
roomToDockServerFallback
|
|
19
|
+
};
|
package/dist/shell/index.d.ts
CHANGED
|
@@ -4,7 +4,6 @@ import type { GlobalAction } from '../controls/actions.js';
|
|
|
4
4
|
import type { AccountMenuItem, BasaltAccountProps } from './account-types.js';
|
|
5
5
|
import type { SidebarSearchActions, SidebarSearchConfig } from './sidebar-search.js';
|
|
6
6
|
import type { MobileNavConfig, SidebarBlock, SidebarSection } from './nav-types.js';
|
|
7
|
-
import type { SizeClass } from './use-size-class.js';
|
|
8
7
|
export { AppSidebar, type AppSidebarProps } from './app-sidebar.js';
|
|
9
8
|
export { NavCountBadge, type NavCountBadgeProps } from './nav-count-badge.js';
|
|
10
9
|
export { SidebarSearch, type SidebarSearchProps, type SidebarSearchConfig, type SidebarSearchActions, } from './sidebar-search.js';
|
|
@@ -136,16 +135,6 @@ export type BasaltShellProps = BasaltProps & {
|
|
|
136
135
|
/** Page content. */
|
|
137
136
|
children?: ReactNode;
|
|
138
137
|
};
|
|
139
|
-
/**
|
|
140
|
-
* The room-to-dock `min-width` query's SSR/no-`matchMedia` fallback — an `'expanded'`
|
|
141
|
-
* `sizeClassHint` means the server already believes there is room, matching `useSizeClass`'s own
|
|
142
|
-
* hint-seeded guard one level up (`use-size-class.ts`). Extracted as its own pure function (rather
|
|
143
|
-
* than the inline `sizeClassHint === 'expanded'` it replaces) purely so `index.test.tsx` can pin it
|
|
144
|
-
* without needing to render `BasaltShell` through a real SSR pass — `AppShell`'s + `PageAside`'s own
|
|
145
|
-
* effects/refs/portals make that path unobservable through rendered markup, and the previous
|
|
146
|
-
* hardcoded `false` fallback here shipped with no test at all for exactly that reason.
|
|
147
|
-
*/
|
|
148
|
-
export declare function roomToDockServerFallback(sizeClassHint: SizeClass | undefined): boolean;
|
|
149
138
|
/**
|
|
150
139
|
* The shell's two page-level regions are providers, and both wrap the frame rather than living
|
|
151
140
|
* inside it: `PageBarProvider` owns the header portal and the single-kebab claim, `AsideProvider`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/shell/index.tsx"],"names":[],"mappings":"AAuBA,OAAO,KAAK,EAAiB,UAAU,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAGjE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,iBAAiB,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/shell/index.tsx"],"names":[],"mappings":"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"}
|
package/dist/shell/index.js
CHANGED
|
@@ -12,10 +12,10 @@ import { blockRowCount, projectMobileNav } from "./mobile-nav-model.js";
|
|
|
12
12
|
import { AppBreadcrumbs } from "./app-breadcrumbs.js";
|
|
13
13
|
import { PageBarBandOutlet, PageBarOutlet, PageBarProvider, usePageKebabClaimed } from "./page-bar.js";
|
|
14
14
|
import { AsideDocksContext, AsideOutlet, AsideProvider, useAsideRegion } from "./page-aside.js";
|
|
15
|
+
import { asideDockQuery, resolveAsideDocking, roomToDockServerFallback } from "./aside-docking.js";
|
|
15
16
|
import { OverflowMenu, globalActionAsBarAction, globalActionMobile } from "../controls/actions.js";
|
|
16
17
|
import { CtlSlot, useBasaltSpacing } from "../theme/index.js";
|
|
17
18
|
import { createPersistedStore } from "../state/persisted.js";
|
|
18
|
-
import { toEm } from "../tokens/size-classes.js";
|
|
19
19
|
import { SizeClassHintContext, useSizeClass } from "./use-size-class.js";
|
|
20
20
|
import brandClasses from "./app-brand.module.css";
|
|
21
21
|
import headerClasses from "./app-header.module.css";
|
|
@@ -73,10 +73,6 @@ function collapseStore(key) {
|
|
|
73
73
|
collapseStores.set(key, store);
|
|
74
74
|
return store;
|
|
75
75
|
}
|
|
76
|
-
const MAIN_MIN_WIDTH = 720;
|
|
77
|
-
function roomToDockServerFallback(sizeClassHint) {
|
|
78
|
-
return sizeClassHint === "expanded";
|
|
79
|
-
}
|
|
80
76
|
function BasaltShell(props) {
|
|
81
77
|
assertRequiredProps("BasaltShell", props, ["brand", "sections"]);
|
|
82
78
|
return /* @__PURE__ */ jsx(PageBarProvider, { globalActions: props.globalActions ?? [], children: /* @__PURE__ */ jsx(AsideProvider, { children: /* @__PURE__ */ jsx(ShellFrame, { ...props }) }) });
|
|
@@ -109,14 +105,18 @@ function ShellFrame({
|
|
|
109
105
|
const navbarWidth = collapsed ? step.appShellNavbarRailWidth : step.appShellNavbarWidth;
|
|
110
106
|
const sizeClassHint = useContext(SizeClassHintContext);
|
|
111
107
|
const roomToDock = useMediaQuery(
|
|
112
|
-
|
|
108
|
+
asideDockQuery({ navbarWidth, asideWidth: step.appShellAsideWidth }),
|
|
113
109
|
roomToDockServerFallback(sizeClassHint)
|
|
114
110
|
);
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
111
|
+
const docking = resolveAsideDocking({
|
|
112
|
+
claimed: aside.claimed,
|
|
113
|
+
folded: aside.folded,
|
|
114
|
+
sizeClass,
|
|
115
|
+
roomToDock,
|
|
116
|
+
asideWidth: step.appShellAsideWidth,
|
|
117
|
+
asideRailWidth: step.appShellAsideRailWidth
|
|
118
|
+
});
|
|
119
|
+
const asideOverlay = docking.overlay;
|
|
120
120
|
const { requestClose } = aside;
|
|
121
121
|
useEffect(() => {
|
|
122
122
|
if (!asideOverlay) return;
|
|
@@ -141,7 +141,7 @@ function ShellFrame({
|
|
|
141
141
|
() => projectMobileNav(sections, { config: mobileNav, extraMoreRows }),
|
|
142
142
|
[sections, mobileNav, extraMoreRows]
|
|
143
143
|
);
|
|
144
|
-
return /* @__PURE__ */ jsx(AsideDocksContext.Provider, { value: docks, children: /* @__PURE__ */ jsxs(
|
|
144
|
+
return /* @__PURE__ */ jsx(AsideDocksContext.Provider, { value: docking.docks, children: /* @__PURE__ */ jsxs(
|
|
145
145
|
AppShell,
|
|
146
146
|
{
|
|
147
147
|
h: "100dvh",
|
|
@@ -157,7 +157,7 @@ function ShellFrame({
|
|
|
157
157
|
},
|
|
158
158
|
aside: {
|
|
159
159
|
// An overlay reserves the rail only — `<AppShell.Aside>` below widens itself over main.
|
|
160
|
-
width:
|
|
160
|
+
width: docking.width,
|
|
161
161
|
breakpoint: "sm",
|
|
162
162
|
// Below `sm` there is no region at all — `PageAside` renders its content in the page
|
|
163
163
|
// flow instead, one node, no responsive twin (law C9).
|
|
@@ -256,7 +256,6 @@ export {
|
|
|
256
256
|
SidebarAccount,
|
|
257
257
|
SidebarSearch,
|
|
258
258
|
projectMobileNav2 as projectMobileNav,
|
|
259
|
-
roomToDockServerFallback,
|
|
260
259
|
useBreakpoint,
|
|
261
260
|
useSizeClass2 as useSizeClass
|
|
262
261
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export type BreakpointName = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
|
|
2
2
|
/**
|
|
3
|
-
* @deprecated Use `useSizeClass` — removed
|
|
3
|
+
* @deprecated Use `useSizeClass` — removed in 1.33.0 (see `MIGRATING.md`). `theme.breakpoints` is
|
|
4
4
|
* now derived: `sm` moved 48em → 52.5em and `md`/`lg`/`xl` all sit at 75em (`xs`/`sm` collapse,
|
|
5
5
|
* `md`/`lg`/`xl` collapse).
|
|
6
6
|
*/
|
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
* `.sheetBody` is the Modal body; it reaches the trailing button row `overlays.confirm`
|
|
43
43
|
* renders (Mantine `Group`), stretching the buttons to full width at the `--vx-hit` floor.
|
|
44
44
|
*/
|
|
45
|
+
/* theme-allow raw-media-query — the bottom sheet IS the compact size class's overlay form */
|
|
45
46
|
@media (max-width: 52.49375em) {
|
|
46
47
|
.sheetInner {
|
|
47
48
|
align-items: flex-end;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"derive-controls.d.ts","sourceRoot":"","sources":["../../src/theme-lab/derive-controls.tsx"],"names":[],"mappings":"AA8DA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAiBtC,OAAO,EAAE,aAAa,EAAE,yBAAyB,EAAE,KAAK,oBAAoB,EAAE,MAAM,gBAAgB,CAAA;AAiDpG,MAAM,MAAM,mBAAmB,GAAG;IAChC,uFAAuF;IACvF,SAAS,CAAC,EAAE,SAAS,CAAA;CACtB,CAAA;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,EAAE,SAAS,EAAE,EAAE,mBAAmB,+
|
|
1
|
+
{"version":3,"file":"derive-controls.d.ts","sourceRoot":"","sources":["../../src/theme-lab/derive-controls.tsx"],"names":[],"mappings":"AA8DA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAiBtC,OAAO,EAAE,aAAa,EAAE,yBAAyB,EAAE,KAAK,oBAAoB,EAAE,MAAM,gBAAgB,CAAA;AAiDpG,MAAM,MAAM,mBAAmB,GAAG;IAChC,uFAAuF;IACvF,SAAS,CAAC,EAAE,SAAS,CAAA;CACtB,CAAA;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,EAAE,SAAS,EAAE,EAAE,mBAAmB,+BA0FhE"}
|
package/llms.txt
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# basalt-ui v1.
|
|
1
|
+
# basalt-ui v1.32.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.32.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.32.0",
|
|
4
4
|
"description": "Opinionated framework for Mantine-based React apps — theme, app shell, visx charts, toolchain presets",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"charts",
|
|
@@ -171,6 +171,7 @@
|
|
|
171
171
|
"@shikijs/themes": "^4.4.3",
|
|
172
172
|
"@tanstack/react-hotkeys": "0.10.0",
|
|
173
173
|
"@tanstack/react-router-devtools": "^1.167.2",
|
|
174
|
+
"@testing-library/react": "16.3.3",
|
|
174
175
|
"@types/bun": "^1.4.2",
|
|
175
176
|
"@types/hast": "^3.0.5",
|
|
176
177
|
"@types/react": "^19.3.0",
|
|
@@ -8,21 +8,15 @@ import type { ChartMargin } from '../../tokens'
|
|
|
8
8
|
import type { CursorResolution } from '../cursor/resolve'
|
|
9
9
|
import { useBandPlot } from '../hooks/useBandPlot'
|
|
10
10
|
import type { BandFold, BandTooltipConfig } from '../hooks/useBandPlot'
|
|
11
|
-
import { AxisBottomDate } from '../primitives/Axes'
|
|
12
11
|
import { ChartFrame, resolveLegend } from '../primitives/ChartFrame'
|
|
13
12
|
import type { ResponsiveChartHeight } from '../primitives/ChartFrame'
|
|
14
|
-
import {
|
|
15
|
-
ChartTooltipFloat,
|
|
16
|
-
TooltipBody,
|
|
17
|
-
TooltipHeader,
|
|
18
|
-
TooltipRow,
|
|
19
|
-
} from '../primitives/ChartTooltip'
|
|
13
|
+
import { TooltipRow } from '../primitives/ChartTooltip'
|
|
20
14
|
import { Crosshair } from '../primitives/Crosshair'
|
|
21
15
|
import { HatchPattern, hatchFill, hatchSizeFor } from '../primitives/HatchPattern'
|
|
22
|
-
import { HoverOverlay } from '../primitives/HoverOverlay'
|
|
23
16
|
import type { ChartState } from '../primitives/ChartPending'
|
|
24
17
|
import type { ChartLegendConfig, SeriesStyle } from '../series'
|
|
25
18
|
import { fmtAxisDate } from '../utils/format'
|
|
19
|
+
import { BandAxisOverlay, BandTooltip, clampFraction } from './band-chrome'
|
|
26
20
|
import { isDev } from '../../common/is-dev'
|
|
27
21
|
|
|
28
22
|
/**
|
|
@@ -256,7 +250,7 @@ function BandStripPlot<T>(props: BandStripPlotProps<T>) {
|
|
|
256
250
|
...(tooltip !== undefined && { tooltip }),
|
|
257
251
|
})
|
|
258
252
|
|
|
259
|
-
const { bands, margin, plotWidth,
|
|
253
|
+
const { bands, margin, plotWidth, point, crosshairX, step, bandWidth } = band
|
|
260
254
|
const stripHeight = Math.max(plot.height - margin.top - margin.bottom, 1)
|
|
261
255
|
const hatchId = `${chartId}-band-absent`
|
|
262
256
|
if (absentState !== undefined && !styleByKey.has(absentState)) {
|
|
@@ -351,8 +345,6 @@ function BandStripPlot<T>(props: BandStripPlotProps<T>) {
|
|
|
351
345
|
|
|
352
346
|
if (plotWidth <= 0 || bands.length === 0) return null
|
|
353
347
|
|
|
354
|
-
const cfg = tooltip === false ? undefined : tooltip
|
|
355
|
-
const badge = cfg?.label === undefined || point === null ? null : cfg.label(point)
|
|
356
348
|
const row = point === null ? null : deriveBandRow(getBand(point), styleByKey, hidden, point)
|
|
357
349
|
|
|
358
350
|
return (
|
|
@@ -383,69 +375,31 @@ function BandStripPlot<T>(props: BandStripPlotProps<T>) {
|
|
|
383
375
|
|
|
384
376
|
{crosshairX !== null && <Crosshair x={crosshairX} top={0} bottom={stripHeight} />}
|
|
385
377
|
|
|
386
|
-
<
|
|
387
|
-
|
|
388
|
-
scale={scale}
|
|
389
|
-
tickValues={band.tickValues}
|
|
390
|
-
tickFormat={(v) => formatX(String(v))}
|
|
391
|
-
anchorTerminals={band.xAnchorTerminals}
|
|
392
|
-
{...(band.xWrapWidth !== undefined && { wrapWidth: band.xWrapWidth })}
|
|
393
|
-
/>
|
|
394
|
-
|
|
395
|
-
<HoverOverlay
|
|
396
|
-
width={plotWidth}
|
|
378
|
+
<BandAxisOverlay
|
|
379
|
+
band={band}
|
|
397
380
|
height={stripHeight}
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
onLeave={cursor.onPointerLeave}
|
|
402
|
-
onKeyDown={cursor.onKeyDown}
|
|
403
|
-
onBlur={cursor.onBlur}
|
|
404
|
-
valueMax={Math.max(bands.length - 1, 0)}
|
|
405
|
-
{...(point !== null && {
|
|
406
|
-
valueNow: bands.indexOf(point),
|
|
407
|
-
valueText: formatX(getX(point)),
|
|
408
|
-
})}
|
|
409
|
-
{...(ariaLabel !== undefined && { ariaLabel })}
|
|
381
|
+
getX={getX}
|
|
382
|
+
formatX={formatX}
|
|
383
|
+
ariaLabel={ariaLabel}
|
|
410
384
|
/>
|
|
411
385
|
</Group>
|
|
412
386
|
</svg>
|
|
413
387
|
|
|
414
|
-
{band
|
|
415
|
-
|
|
416
|
-
<
|
|
417
|
-
|
|
418
|
-
{
|
|
419
|
-
|
|
420
|
-
}
|
|
421
|
-
{
|
|
388
|
+
<BandTooltip band={band} tooltip={tooltip} getX={getX} hidden={hidden}>
|
|
389
|
+
{row !== null && (
|
|
390
|
+
<TooltipRow
|
|
391
|
+
color={row.color}
|
|
392
|
+
label={row.label}
|
|
393
|
+
value={row.value}
|
|
394
|
+
shape={row.shape}
|
|
395
|
+
dashed={row.dashed}
|
|
422
396
|
/>
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
{row !== null && (
|
|
426
|
-
<TooltipRow
|
|
427
|
-
color={row.color}
|
|
428
|
-
label={row.label}
|
|
429
|
-
value={row.value}
|
|
430
|
-
shape={row.shape}
|
|
431
|
-
dashed={row.dashed}
|
|
432
|
-
/>
|
|
433
|
-
)}
|
|
434
|
-
{cfg?.extraRows?.(point, { hidden })}
|
|
435
|
-
</TooltipBody>
|
|
436
|
-
</ChartTooltipFloat>
|
|
437
|
-
)}
|
|
397
|
+
)}
|
|
398
|
+
</BandTooltip>
|
|
438
399
|
</>
|
|
439
400
|
)
|
|
440
401
|
}
|
|
441
402
|
|
|
442
|
-
/** A 0..1 share. Non-finite (a 0/0 fold count) reads as "nothing is absent" — the conservative
|
|
443
|
-
* end, since the alternative is a band that does not render at all. */
|
|
444
|
-
function clampFraction(value: number | undefined): number {
|
|
445
|
-
if (value === undefined || !Number.isFinite(value)) return 0
|
|
446
|
-
return Math.min(Math.max(value, 0), 1)
|
|
447
|
-
}
|
|
448
|
-
|
|
449
403
|
/**
|
|
450
404
|
* The strip's one derived row: the hovered band's STATE, named and formatted by its own `series`
|
|
451
405
|
* entry. A strip cannot show a row for a state it does not draw, and cannot name a state the
|
|
@@ -13,11 +13,12 @@ import {
|
|
|
13
13
|
TooltipBody,
|
|
14
14
|
TooltipHeader,
|
|
15
15
|
TooltipRow,
|
|
16
|
+
TooltipRows,
|
|
16
17
|
} from '../primitives/ChartTooltip'
|
|
17
18
|
import { ChartFrame, resolveLegend } from '../primitives/ChartFrame'
|
|
18
19
|
import type { ResponsiveChartHeight } from '../primitives/ChartFrame'
|
|
19
20
|
import { Crosshair, SeriesDot } from '../primitives/Crosshair'
|
|
20
|
-
import { HoverOverlay } from '../primitives/HoverOverlay'
|
|
21
|
+
import { cursorSliderProps, HoverOverlay } from '../primitives/HoverOverlay'
|
|
21
22
|
import { ZoneRects } from '../primitives/ZoneRects'
|
|
22
23
|
import type { ZoneSpec } from '../primitives/ZoneRects'
|
|
23
24
|
import type { ChartState } from '../primitives/ChartPending'
|
|
@@ -595,18 +596,7 @@ function DualPanelPlot<T>(props: DualPanelPlotProps<T>) {
|
|
|
595
596
|
<HoverOverlay
|
|
596
597
|
width={xMax}
|
|
597
598
|
height={topH + PANE_GAP}
|
|
598
|
-
|
|
599
|
-
onDown={cursor.onPointerDown}
|
|
600
|
-
onUp={cursor.onPointerUp}
|
|
601
|
-
onLeave={cursor.onPointerLeave}
|
|
602
|
-
onKeyDown={cursor.onKeyDown}
|
|
603
|
-
onBlur={cursor.onBlur}
|
|
604
|
-
valueMax={Math.max(data.length - 1, 0)}
|
|
605
|
-
{...(point !== null && {
|
|
606
|
-
valueNow: data.indexOf(point),
|
|
607
|
-
valueText: formatX(getX(point)),
|
|
608
|
-
})}
|
|
609
|
-
{...(ariaLabel !== undefined && { ariaLabel })}
|
|
599
|
+
{...cursorSliderProps({ cursor, data, getX, formatX, ariaLabel })}
|
|
610
600
|
/>
|
|
611
601
|
</Group>
|
|
612
602
|
|
|
@@ -688,17 +678,7 @@ function DualPanelPlot<T>(props: DualPanelPlotProps<T>) {
|
|
|
688
678
|
})()}
|
|
689
679
|
/>
|
|
690
680
|
<TooltipBody>
|
|
691
|
-
{deriveTooltipRows(visibleSeries, point, formatTop)
|
|
692
|
-
<TooltipRow
|
|
693
|
-
key={row.key}
|
|
694
|
-
color={row.color}
|
|
695
|
-
label={row.label}
|
|
696
|
-
value={row.value}
|
|
697
|
-
shape={row.shape}
|
|
698
|
-
dashed={row.dashed}
|
|
699
|
-
{...(row.strokeWidth !== undefined && { strokeWidth: row.strokeWidth })}
|
|
700
|
-
/>
|
|
701
|
-
))}
|
|
681
|
+
<TooltipRows rows={deriveTooltipRows(visibleSeries, point, formatTop)} />
|
|
702
682
|
{barVisible &&
|
|
703
683
|
(() => {
|
|
704
684
|
const v = getBar(point)
|
|
@@ -10,22 +10,17 @@ import type { CursorResolution } from '../cursor/resolve'
|
|
|
10
10
|
import { useBandPlot } from '../hooks/useBandPlot'
|
|
11
11
|
import type { BandFold, BandTooltipConfig } from '../hooks/useBandPlot'
|
|
12
12
|
import { probeAxisLabels } from '../layout/auto-margin'
|
|
13
|
-
import {
|
|
13
|
+
import { AxisLeftNumeric } from '../primitives/Axes'
|
|
14
14
|
import { ChartFrame, resolveLegend } from '../primitives/ChartFrame'
|
|
15
15
|
import type { ResponsiveChartHeight } from '../primitives/ChartFrame'
|
|
16
|
-
import {
|
|
17
|
-
ChartTooltipFloat,
|
|
18
|
-
TooltipBody,
|
|
19
|
-
TooltipHeader,
|
|
20
|
-
TooltipRow,
|
|
21
|
-
} from '../primitives/ChartTooltip'
|
|
16
|
+
import { TooltipRows } from '../primitives/ChartTooltip'
|
|
22
17
|
import { Crosshair } from '../primitives/Crosshair'
|
|
23
18
|
import { HatchPattern, hatchFill, hatchSizeFor } from '../primitives/HatchPattern'
|
|
24
|
-
import { HoverOverlay } from '../primitives/HoverOverlay'
|
|
25
19
|
import type { ChartState } from '../primitives/ChartPending'
|
|
26
20
|
import { deriveTooltipRows } from '../series'
|
|
27
21
|
import type { ChartLegendConfig, ChartSeries } from '../series'
|
|
28
22
|
import { fmtAxisDate } from '../utils/format'
|
|
23
|
+
import { BandAxisOverlay, BandTooltip, clampFraction } from './band-chrome'
|
|
29
24
|
|
|
30
25
|
/** One of the two panes. Each resolves its own domain — that is the whole point of the kind. */
|
|
31
26
|
export type MirroredBarPane = {
|
|
@@ -242,7 +237,7 @@ function MirroredBarsPlot<T>(props: MirroredBarsPlotProps<T>) {
|
|
|
242
237
|
...(tooltip !== undefined && { tooltip }),
|
|
243
238
|
})
|
|
244
239
|
|
|
245
|
-
const { bands, margin, plotWidth,
|
|
240
|
+
const { bands, margin, plotWidth, point, crosshairX, step, bandWidth } = band
|
|
246
241
|
|
|
247
242
|
// ── Pass 2: the pane rects, now that the plot height is known ───────────────────────────────
|
|
248
243
|
const barBand = Math.max(plot.height - margin.top - margin.bottom, 2)
|
|
@@ -346,8 +341,6 @@ function MirroredBarsPlot<T>(props: MirroredBarsPlotProps<T>) {
|
|
|
346
341
|
|
|
347
342
|
if (plotWidth <= 0 || bands.length === 0) return null
|
|
348
343
|
|
|
349
|
-
const cfg = tooltip === false ? undefined : tooltip
|
|
350
|
-
const badge = cfg?.label === undefined || point === null ? null : cfg.label(point)
|
|
351
344
|
// Each pane's row falls back to ITS OWN axis formatter, never the other pane's — the two panes
|
|
352
345
|
// are in different units by construction (an explicit per-series `formatValue` still wins).
|
|
353
346
|
const visibleSeries = series
|
|
@@ -419,70 +412,23 @@ function MirroredBarsPlot<T>(props: MirroredBarsPlotProps<T>) {
|
|
|
419
412
|
|
|
420
413
|
{crosshairX !== null && <Crosshair x={crosshairX} top={0} bottom={barBand} />}
|
|
421
414
|
|
|
422
|
-
<
|
|
423
|
-
|
|
424
|
-
scale={scale}
|
|
425
|
-
tickValues={band.tickValues}
|
|
426
|
-
tickFormat={(v) => formatX(String(v))}
|
|
427
|
-
anchorTerminals={band.xAnchorTerminals}
|
|
428
|
-
{...(band.xWrapWidth !== undefined && { wrapWidth: band.xWrapWidth })}
|
|
429
|
-
/>
|
|
430
|
-
|
|
431
|
-
<HoverOverlay
|
|
432
|
-
width={plotWidth}
|
|
415
|
+
<BandAxisOverlay
|
|
416
|
+
band={band}
|
|
433
417
|
height={barBand}
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
onLeave={cursor.onPointerLeave}
|
|
438
|
-
onKeyDown={cursor.onKeyDown}
|
|
439
|
-
onBlur={cursor.onBlur}
|
|
440
|
-
valueMax={Math.max(bands.length - 1, 0)}
|
|
441
|
-
{...(point !== null && {
|
|
442
|
-
valueNow: bands.indexOf(point),
|
|
443
|
-
valueText: formatX(getX(point)),
|
|
444
|
-
})}
|
|
445
|
-
{...(ariaLabel !== undefined && { ariaLabel })}
|
|
418
|
+
getX={getX}
|
|
419
|
+
formatX={formatX}
|
|
420
|
+
ariaLabel={ariaLabel}
|
|
446
421
|
/>
|
|
447
422
|
</Group>
|
|
448
423
|
</svg>
|
|
449
424
|
|
|
450
|
-
{band
|
|
451
|
-
<
|
|
452
|
-
|
|
453
|
-
date={getX(point)}
|
|
454
|
-
{...(cfg?.formatHeader !== undefined && {
|
|
455
|
-
format: (key: string) => cfg.formatHeader?.(key, point) ?? key,
|
|
456
|
-
})}
|
|
457
|
-
{...(badge !== null && { label: badge.text, labelColor: badge.color })}
|
|
458
|
-
/>
|
|
459
|
-
<TooltipBody>
|
|
460
|
-
{cfg?.prependRows?.(point, { hidden })}
|
|
461
|
-
{rows.map((row) => (
|
|
462
|
-
<TooltipRow
|
|
463
|
-
key={row.key}
|
|
464
|
-
color={row.color}
|
|
465
|
-
label={row.label}
|
|
466
|
-
value={row.value}
|
|
467
|
-
shape={row.shape}
|
|
468
|
-
dashed={row.dashed}
|
|
469
|
-
{...(row.strokeWidth !== undefined && { strokeWidth: row.strokeWidth })}
|
|
470
|
-
/>
|
|
471
|
-
))}
|
|
472
|
-
{cfg?.extraRows?.(point, { hidden })}
|
|
473
|
-
</TooltipBody>
|
|
474
|
-
</ChartTooltipFloat>
|
|
475
|
-
)}
|
|
425
|
+
<BandTooltip band={band} tooltip={tooltip} getX={getX} hidden={hidden}>
|
|
426
|
+
<TooltipRows rows={rows} />
|
|
427
|
+
</BandTooltip>
|
|
476
428
|
</>
|
|
477
429
|
)
|
|
478
430
|
}
|
|
479
431
|
|
|
480
|
-
/** A 0..1 share, non-finite included — see `BandStrip`'s identical guard. */
|
|
481
|
-
function clampFraction(value: number | undefined): number {
|
|
482
|
-
if (value === undefined || !Number.isFinite(value)) return 0
|
|
483
|
-
return Math.min(Math.max(value, 0), 1)
|
|
484
|
-
}
|
|
485
|
-
|
|
486
432
|
/** A drawable value, or null. See the call site for why NaN is not drawable. */
|
|
487
433
|
function finiteOrNull(v: number | null | undefined): number | null {
|
|
488
434
|
return v === null || v === undefined || Number.isNaN(v) ? null : v
|