basalt-ui 1.31.0 → 1.32.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/CHANGELOG.md +34 -0
  2. package/MIGRATING.md +212 -20
  3. package/agent/rules/basalt-mantine.md +4 -4
  4. package/configs/oxlint-plugin.js +240 -59
  5. package/configs/oxlint-plugin.test.ts +329 -1
  6. package/dist/charts/kinds/BandStrip.d.ts.map +1 -1
  7. package/dist/charts/kinds/BandStrip.js +18 -66
  8. package/dist/charts/kinds/DualPanel.d.ts.map +1 -1
  9. package/dist/charts/kinds/DualPanel.js +5 -26
  10. package/dist/charts/kinds/MirroredBars.d.ts.map +1 -1
  11. package/dist/charts/kinds/MirroredBars.js +10 -68
  12. package/dist/charts/kinds/band-chrome.d.ts +31 -0
  13. package/dist/charts/kinds/band-chrome.d.ts.map +1 -0
  14. package/dist/charts/kinds/band-chrome.js +71 -0
  15. package/dist/charts/primitives/CartesianChart.d.ts.map +1 -1
  16. package/dist/charts/primitives/CartesianChart.js +49 -76
  17. package/dist/charts/primitives/ChartCard.d.ts.map +1 -1
  18. package/dist/charts/primitives/ChartCard.js +5 -23
  19. package/dist/charts/primitives/ChartFrame.d.ts.map +1 -1
  20. package/dist/charts/primitives/ChartFrame.js +23 -40
  21. package/dist/charts/primitives/ChartLegend.d.ts.map +1 -1
  22. package/dist/charts/primitives/ChartLegend.js +5 -133
  23. package/dist/charts/primitives/ChartTooltip.d.ts +5 -0
  24. package/dist/charts/primitives/ChartTooltip.d.ts.map +1 -1
  25. package/dist/charts/primitives/ChartTooltip.js +16 -1
  26. package/dist/charts/primitives/HoverOverlay.d.ts +24 -9
  27. package/dist/charts/primitives/HoverOverlay.d.ts.map +1 -1
  28. package/dist/charts/primitives/HoverOverlay.js +17 -1
  29. package/dist/charts/primitives/chart-card-context.d.ts +17 -13
  30. package/dist/charts/primitives/chart-card-context.d.ts.map +1 -1
  31. package/dist/charts/primitives/chart-card-context.js +10 -5
  32. package/dist/charts/primitives/chart-frame-layout.d.ts.map +1 -1
  33. package/dist/charts/primitives/chart-frame-layout.js +25 -31
  34. package/dist/charts/primitives/legend-disclosure.d.ts +27 -0
  35. package/dist/charts/primitives/legend-disclosure.d.ts.map +1 -0
  36. package/dist/charts/primitives/legend-disclosure.js +153 -0
  37. package/dist/controls/controls.module.css +5 -0
  38. package/dist/controls/sync-button.module.css +1 -0
  39. package/dist/dashboard/stat-group.module.css +19 -64
  40. package/dist/dashboard/widget-grid.module.css +11 -38
  41. package/dist/data/data-table-state.d.ts +83 -0
  42. package/dist/data/data-table-state.d.ts.map +1 -0
  43. package/dist/data/data-table-state.js +139 -0
  44. package/dist/data/data-table.d.ts +40 -2
  45. package/dist/data/data-table.d.ts.map +1 -1
  46. package/dist/data/data-table.js +179 -277
  47. package/dist/forms/form-layout.d.ts +6 -1
  48. package/dist/forms/form-layout.d.ts.map +1 -1
  49. package/dist/forms/form-layout.module.css +6 -13
  50. package/dist/guard/index.d.ts +2 -2
  51. package/dist/guard/index.d.ts.map +1 -1
  52. package/dist/guard/index.js +10 -14
  53. package/dist/notifications/center.d.ts.map +1 -1
  54. package/dist/provider/index.d.ts.map +1 -1
  55. package/dist/provider/index.js +12 -10
  56. package/dist/shell/aside-docking.d.ts +31 -0
  57. package/dist/shell/aside-docking.d.ts.map +1 -0
  58. package/dist/shell/aside-docking.js +19 -0
  59. package/dist/shell/index.d.ts +0 -11
  60. package/dist/shell/index.d.ts.map +1 -1
  61. package/dist/shell/index.js +13 -14
  62. package/dist/shell/page-bar.d.ts +24 -0
  63. package/dist/shell/page-bar.d.ts.map +1 -1
  64. package/dist/shell/use-breakpoint.d.ts +1 -1
  65. package/dist/theme/floating.module.css +1 -0
  66. package/dist/theme-lab/derive-controls.d.ts.map +1 -1
  67. package/dist/tokens/index.d.ts +2 -0
  68. package/dist/tokens/index.d.ts.map +1 -1
  69. package/dist/tokens/index.js +4 -0
  70. package/llms.txt +2 -2
  71. package/package.json +2 -1
  72. package/src/charts/kinds/BandStrip.tsx +18 -64
  73. package/src/charts/kinds/DualPanel.tsx +4 -24
  74. package/src/charts/kinds/MirroredBars.tsx +12 -66
  75. package/src/charts/kinds/band-chrome.tsx +91 -0
  76. package/src/charts/primitives/CartesianChart.tsx +79 -122
  77. package/src/charts/primitives/ChartCard.tsx +5 -27
  78. package/src/charts/primitives/ChartFrame.tsx +37 -76
  79. package/src/charts/primitives/ChartLegend.tsx +7 -154
  80. package/src/charts/primitives/ChartTooltip.tsx +16 -0
  81. package/src/charts/primitives/HoverOverlay.tsx +52 -21
  82. package/src/charts/primitives/chart-card-context.ts +25 -16
  83. package/src/charts/primitives/chart-frame-layout.ts +44 -56
  84. package/src/charts/primitives/legend-disclosure.tsx +177 -0
  85. package/src/controls/controls.module.css +5 -0
  86. package/src/controls/sync-button.module.css +1 -0
  87. package/src/dashboard/stat-group.module.css +19 -64
  88. package/src/dashboard/widget-grid.module.css +11 -38
  89. package/src/data/data-table-state.ts +218 -0
  90. package/src/data/data-table.tsx +134 -218
  91. package/src/forms/form-layout.module.css +6 -13
  92. package/src/forms/form-layout.tsx +6 -1
  93. package/src/guard/index.ts +38 -34
  94. package/src/notifications/center.tsx +3 -0
  95. package/src/provider/index.tsx +18 -17
  96. package/src/shell/aside-docking.ts +54 -0
  97. package/src/shell/index.tsx +15 -37
  98. package/src/shell/page-bar.tsx +24 -0
  99. package/src/shell/use-breakpoint.ts +2 -2
  100. package/src/theme/floating.module.css +1 -0
  101. package/src/theme-lab/derive-controls.tsx +2 -0
  102. package/src/tokens/index.ts +5 -0
@@ -1,10 +1,8 @@
1
1
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
2
- import { useCallback, useEffect, useId, useRef, useState } from "react";
3
- import { createPortal } from "react-dom";
4
2
  import { alpha, VX } from "../../tokens/index.js";
5
- import { useDismissOnOutside } from "../cursor/touch-pin.js";
6
3
  import { DOTS_HIT_GAP, LEGEND_DOT_SIZE, LEGEND_ROW_GAP, orderEntries } from "./chart-frame-layout.js";
7
4
  import { useChartMetrics, useCoarsePointer } from "./chart-tier.js";
5
+ import { LegendDisclosure, useLegendDisclosure } from "./legend-disclosure.js";
8
6
  const LEGEND_ITEM_BASE = {
9
7
  display: "flex",
10
8
  alignItems: "center",
@@ -24,48 +22,6 @@ const LEGEND_ITEM_BUTTON = {
24
22
  textAlign: "inherit"
25
23
  };
26
24
  const NO_HIDDEN = /* @__PURE__ */ new Set();
27
- const DISCLOSURE_PANEL = {
28
- position: "fixed",
29
- zIndex: VX.zIndexFloating,
30
- display: "flex",
31
- flexDirection: "column",
32
- gap: LEGEND_ROW_GAP,
33
- // theme-allow raw-scroll-container — a fixed floating panel of one legend, not app chrome
34
- overflowY: "auto",
35
- // `<dialog open>` brings UA chrome (centring margin, border, fit-content box) this resets.
36
- margin: 0,
37
- border: "none",
38
- width: "auto",
39
- height: "auto",
40
- padding: 10,
41
- boxSizing: "border-box",
42
- backgroundColor: VX.surface.overlay,
43
- boxShadow: VX.shadowOverlay,
44
- color: VX.muted,
45
- borderRadius: VX.radiusCard
46
- };
47
- const DISCLOSURE_MIN_HEIGHT = 96;
48
- function disclosurePlacement(chip, coarse) {
49
- if (coarse) {
50
- return {
51
- left: 0,
52
- right: 0,
53
- bottom: 0,
54
- maxHeight: "60dvh",
55
- borderBottomLeftRadius: 0,
56
- borderBottomRightRadius: 0,
57
- padding: "12px 16px calc(12px + env(safe-area-inset-bottom, 0px))"
58
- };
59
- }
60
- const below = window.innerHeight - chip.bottom;
61
- const left = Math.max(8, Math.min(chip.left, window.innerWidth - 248));
62
- const base = { left, right: "auto", minWidth: 200, maxWidth: "calc(100vw - 16px)" };
63
- return below >= 200 || below >= chip.top ? { ...base, top: chip.bottom + 4, maxHeight: Math.max(below - 12, DISCLOSURE_MIN_HEIGHT) } : {
64
- ...base,
65
- bottom: window.innerHeight - chip.top + 4,
66
- maxHeight: Math.max(chip.top - 12, DISCLOSURE_MIN_HEIGHT)
67
- };
68
- }
69
25
  function wrapperStyle(placement, fontSize, dots) {
70
26
  const vertical = placement === "left" || placement === "right";
71
27
  const gap = dots ? DOTS_HIT_GAP : VX.legendGap;
@@ -252,55 +208,7 @@ function ChartLegend({
252
208
  }) {
253
209
  const tier = useChartMetrics();
254
210
  const coarse = useCoarsePointer();
255
- const [disclosure, setDisclosure] = useState(null);
256
- const triggerRef = useRef(null);
257
- const panelRef = useRef(null);
258
- const open = disclosure !== null;
259
- const panelId = useId();
260
- const wasOpen = useRef(false);
261
- const openDisclosure = (trigger) => {
262
- triggerRef.current = trigger;
263
- setDisclosure(disclosurePlacement(trigger.getBoundingClientRect(), coarse));
264
- };
265
- useEffect(() => {
266
- if (open) panelRef.current?.focus();
267
- else if (wasOpen.current) triggerRef.current?.focus();
268
- wasOpen.current = open;
269
- }, [open]);
270
- const isInsidePanel = useCallback(
271
- (target) => panelRef.current?.contains(target) === true || triggerRef.current?.contains(target) === true,
272
- []
273
- );
274
- useDismissOnOutside({
275
- enabled: open,
276
- isInside: isInsidePanel,
277
- onDismiss: () => setDisclosure(null),
278
- dismissOnResize: true,
279
- scrollExemptInside: true
280
- });
281
- useEffect(() => {
282
- if (!open || !coarse) return;
283
- const panel = panelRef.current;
284
- if (panel === null) return;
285
- const onKeyDown = (e) => {
286
- if (e.key !== "Tab") return;
287
- const focusable = panel.querySelectorAll(
288
- 'button, [tabindex]:not([tabindex="-1"])'
289
- );
290
- const first = focusable[0];
291
- const last = focusable[focusable.length - 1];
292
- if (first === void 0 || last === void 0) return;
293
- if (e.shiftKey && document.activeElement === first) {
294
- e.preventDefault();
295
- last.focus();
296
- } else if (!e.shiftKey && document.activeElement === last) {
297
- e.preventDefault();
298
- first.focus();
299
- }
300
- };
301
- panel.addEventListener("keydown", onKeyDown);
302
- return () => panel.removeEventListener("keydown", onKeyDown);
303
- }, [open, coarse]);
211
+ const { open, panelId, openFrom, close, panel } = useLegendDisclosure(coarse);
304
212
  const handleEnter = (key) => onHighlight?.(key);
305
213
  const handleLeave = () => onHighlight?.(null);
306
214
  const idPrefix = chartId ? `${chartId}-` : "";
@@ -327,7 +235,7 @@ function ChartLegend({
327
235
  opacity: hidden.has(item.key) ? 0.35 : highlighted === null || highlighted === item.key ? 1 : 0.3,
328
236
  textDecoration: hidden.has(item.key) ? "line-through" : "none"
329
237
  },
330
- ...isDotTrigger ? { onClick: (e) => openDisclosure(e.currentTarget) } : onToggle !== void 0 && { onClick: () => onToggle(item.key) },
238
+ ...isDotTrigger ? { onClick: (e) => openFrom(e.currentTarget) } : onToggle !== void 0 && { onClick: () => onToggle(item.key) },
331
239
  onMouseEnter: () => handleEnter(item.key),
332
240
  onMouseLeave: handleLeave,
333
241
  onFocus: () => handleEnter(item.key),
@@ -358,7 +266,7 @@ function ChartLegend({
358
266
  "aria-expanded": open,
359
267
  "aria-haspopup": "dialog",
360
268
  "aria-controls": panelId,
361
- onClick: (e) => open ? setDisclosure(null) : openDisclosure(e.currentTarget),
269
+ onClick: (e) => open ? close() : openFrom(e.currentTarget),
362
270
  style: { ...LEGEND_ITEM_BUTTON, cursor: "pointer", textDecoration: "underline" },
363
271
  children: `All ${entries.length}`
364
272
  },
@@ -373,43 +281,7 @@ function ChartLegend({
373
281
  style: { ...wrapperStyle(placement, tier.legendFontSize, mode === "dots"), ...style },
374
282
  children: [
375
283
  nodes,
376
- open && createPortal(
377
- /* @__PURE__ */ jsxs(Fragment, { children: [
378
- coarse && /* @__PURE__ */ jsx(
379
- "div",
380
- {
381
- "aria-hidden": true,
382
- onPointerDown: (e) => {
383
- e.stopPropagation();
384
- setDisclosure(null);
385
- },
386
- style: {
387
- position: "fixed",
388
- inset: 0,
389
- // One below the panel it sits behind — still `VX.zIndexFloating`-derived, never a
390
- // second unrelated literal (B14).
391
- zIndex: VX.zIndexFloating - 1,
392
- backgroundColor: alpha(VX.neutral, 0.35)
393
- }
394
- }
395
- ),
396
- /* @__PURE__ */ jsx(
397
- "dialog",
398
- {
399
- ref: panelRef,
400
- id: panelId,
401
- open: true,
402
- tabIndex: -1,
403
- "aria-label": "Legend",
404
- ...coarse && { "aria-modal": true },
405
- "data-basalt-legend-disclosure": coarse ? "sheet" : "popover",
406
- style: { ...DISCLOSURE_PANEL, fontSize: tier.legendFontSize, ...disclosure },
407
- children: entries.map((item) => entryButton(item, true))
408
- }
409
- )
410
- ] }),
411
- document.body
412
- )
284
+ /* @__PURE__ */ jsx(LegendDisclosure, { ...panel, fontSize: tier.legendFontSize, children: entries.map((item) => entryButton(item, true)) })
413
285
  ]
414
286
  }
415
287
  );
@@ -1,4 +1,5 @@
1
1
  import type { ReactNode } from 'react';
2
+ import type { TooltipRowData } from '../series.js';
2
3
  /** Tooltip header — shows formatted date + optional right-aligned label with color. */
3
4
  export declare function TooltipHeader({ date, label, labelColor, format, }: {
4
5
  date: string;
@@ -21,6 +22,10 @@ export declare function TooltipRow({ color, label, value, valueColor, shape, str
21
22
  /** Render the line swatch as dashed (only applies to shape='line'). */
22
23
  dashed?: boolean;
23
24
  }): import("react").JSX.Element;
25
+ /** The derived rows (`deriveTooltipRows`), one {@link TooltipRow} each. */
26
+ export declare function TooltipRows({ rows }: {
27
+ rows: readonly TooltipRowData[];
28
+ }): import("react").JSX.Element[];
24
29
  export declare function TooltipBody({ children }: {
25
30
  children: ReactNode;
26
31
  }): import("react").JSX.Element;
@@ -1 +1 @@
1
- {"version":3,"file":"ChartTooltip.d.ts","sourceRoot":"","sources":["../../../src/charts/primitives/ChartTooltip.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,SAAS,EAAE,MAAM,OAAO,CAAA;AAuBrD,uFAAuF;AACvF,wBAAgB,aAAa,CAAC,EAC5B,IAAI,EACJ,KAAK,EACL,UAAU,EACV,MAAuB,GACxB,EAAE;IACD,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;sGAGkG;IAClG,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAA;CAClC,+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;AAsBD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,MAAM,EACN,QAAe,EACf,QAAQ,GACT,EAAE;IACD,MAAM,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;IACvC;;;8CAG0C;IAC1C,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,QAAQ,EAAE,SAAS,CAAA;CACpB,sCA2DA"}
1
+ {"version":3,"file":"ChartTooltip.d.ts","sourceRoot":"","sources":["../../../src/charts/primitives/ChartTooltip.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,SAAS,EAAE,MAAM,OAAO,CAAA;AAIrD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAoB/C,uFAAuF;AACvF,wBAAgB,aAAa,CAAC,EAC5B,IAAI,EACJ,KAAK,EACL,UAAU,EACV,MAAuB,GACxB,EAAE;IACD,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;sGAGkG;IAClG,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,MAAM,CAAA;CAClC,+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,2EAA2E;AAC3E,wBAAgB,WAAW,CAAC,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,EAAE,SAAS,cAAc,EAAE,CAAA;CAAE,iCAYxE;AAED,wBAAgB,WAAW,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,SAAS,CAAA;CAAE,+BAEhE;AAsBD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,MAAM,EACN,QAAe,EACf,QAAQ,GACT,EAAE;IACD,MAAM,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAA;IACvC;;;8CAG0C;IAC1C,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,QAAQ,EAAE,SAAS,CAAA;CACpB,sCA2DA"}
@@ -114,6 +114,20 @@ function TooltipRow({
114
114
  }
115
115
  );
116
116
  }
117
+ function TooltipRows({ rows }) {
118
+ return rows.map((row) => /* @__PURE__ */ jsx(
119
+ TooltipRow,
120
+ {
121
+ color: row.color,
122
+ label: row.label,
123
+ value: row.value,
124
+ shape: row.shape,
125
+ dashed: row.dashed,
126
+ ...row.strokeWidth !== void 0 && { strokeWidth: row.strokeWidth }
127
+ },
128
+ row.key
129
+ ));
130
+ }
117
131
  function TooltipBody({ children }) {
118
132
  return /* @__PURE__ */ jsx("div", { style: { padding: "5px 0" }, children });
119
133
  }
@@ -178,5 +192,6 @@ export {
178
192
  ChartTooltipFloat,
179
193
  TooltipBody,
180
194
  TooltipHeader,
181
- TooltipRow
195
+ TooltipRow,
196
+ TooltipRows
182
197
  };
@@ -1,12 +1,7 @@
1
1
  import type { FocusEventHandler, KeyboardEventHandler, PointerEventHandler } from 'react';
2
- /**
3
- * Transparent <rect> that captures pointer events (mouse + touch + pen) for tooltip + crosshair
4
- * sync.
5
- *
6
- * With `onKeyDown` it also becomes the chart's keyboard affordance: the rect takes focus and
7
- * ←/→ scrub the cursor, so a chart's values are reachable without a pointer.
8
- */
9
- export declare function HoverOverlay({ width, height, onMove, onDown, onUp, onLeave, onKeyDown, onBlur, ariaLabel, valueNow, valueMax, valueText, }: {
2
+ import type { ChartCursor } from '../hooks/useChartCursor.js';
3
+ /** Props of {@link HoverOverlay}. */
4
+ export type HoverOverlayProps = {
10
5
  width: number;
11
6
  height: number;
12
7
  onMove: PointerEventHandler<SVGRectElement>;
@@ -26,5 +21,25 @@ export declare function HoverOverlay({ width, height, onMove, onDown, onUp, onLe
26
21
  valueMax?: number;
27
22
  /** Human-readable label for the focused point (the formatted x key). */
28
23
  valueText?: string;
29
- }): import("react").JSX.Element;
24
+ };
25
+ /**
26
+ * Transparent <rect> that captures pointer events (mouse + touch + pen) for tooltip + crosshair
27
+ * sync.
28
+ *
29
+ * With `onKeyDown` it also becomes the chart's keyboard affordance: the rect takes focus and
30
+ * ←/→ scrub the cursor, so a chart's values are reachable without a pointer.
31
+ */
32
+ export declare function HoverOverlay({ width, height, onMove, onDown, onUp, onLeave, onKeyDown, onBlur, ariaLabel, valueNow, valueMax, valueText, }: HoverOverlayProps): import("react").JSX.Element;
33
+ /**
34
+ * Everything a FOCUSABLE overlay over indexed data takes besides its rect: the cursor's pointer and
35
+ * keyboard handlers plus the slider label/position. `CartesianChart`, `DualPanel`'s top pane and
36
+ * the banded kinds all wire it identically, so it is stated once.
37
+ */
38
+ export declare function cursorSliderProps<T>(input: {
39
+ cursor: ChartCursor<T>;
40
+ data: readonly T[];
41
+ getX: (d: T) => string;
42
+ formatX: (key: string) => string;
43
+ ariaLabel: string | undefined;
44
+ }): Omit<HoverOverlayProps, 'width' | 'height'>;
30
45
  //# 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,iBAAiB,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,OAAO,CAAA;AAGzF;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,EAC3B,KAAK,EACL,MAAM,EACN,MAAM,EACN,MAAM,EACN,IAAI,EACJ,OAAO,EACP,SAAS,EACT,MAAM,EACN,SAAS,EACT,QAAQ,EACR,QAAQ,EACR,SAAS,GACV,EAAE;IACD,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,mBAAmB,CAAC,cAAc,CAAC,CAAA;IAC3C;2FACuF;IACvF,MAAM,CAAC,EAAE,mBAAmB,CAAC,cAAc,CAAC,CAAA;IAC5C,wEAAwE;IACxE,IAAI,CAAC,EAAE,mBAAmB,CAAC,cAAc,CAAC,CAAA;IAC1C,OAAO,EAAE,mBAAmB,CAAC,cAAc,CAAC,CAAA;IAC5C,4DAA4D;IAC5D,SAAS,CAAC,EAAE,oBAAoB,CAAC,cAAc,CAAC,CAAA;IAChD,MAAM,CAAC,EAAE,iBAAiB,CAAC,cAAc,CAAC,CAAA;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,qEAAqE;IACrE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,gCAAgC;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,wEAAwE;IACxE,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,+BAiCA"}
1
+ {"version":3,"file":"HoverOverlay.d.ts","sourceRoot":"","sources":["../../../src/charts/primitives/HoverOverlay.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,OAAO,CAAA;AACzF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAA;AAG1D,qCAAqC;AACrC,MAAM,MAAM,iBAAiB,GAAG;IAC9B,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,mBAAmB,CAAC,cAAc,CAAC,CAAA;IAC3C;2FACuF;IACvF,MAAM,CAAC,EAAE,mBAAmB,CAAC,cAAc,CAAC,CAAA;IAC5C,wEAAwE;IACxE,IAAI,CAAC,EAAE,mBAAmB,CAAC,cAAc,CAAC,CAAA;IAC1C,OAAO,EAAE,mBAAmB,CAAC,cAAc,CAAC,CAAA;IAC5C,4DAA4D;IAC5D,SAAS,CAAC,EAAE,oBAAoB,CAAC,cAAc,CAAC,CAAA;IAChD,MAAM,CAAC,EAAE,iBAAiB,CAAC,cAAc,CAAC,CAAA;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,qEAAqE;IACrE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,gCAAgC;IAChC,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,wEAAwE;IACxE,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB,CAAA;AAED;;;;;;GAMG;AACH,wBAAgB,YAAY,CAAC,EAC3B,KAAK,EACL,MAAM,EACN,MAAM,EACN,MAAM,EACN,IAAI,EACJ,OAAO,EACP,SAAS,EACT,MAAM,EACN,SAAS,EACT,QAAQ,EACR,QAAQ,EACR,SAAS,GACV,EAAE,iBAAiB,+BAiCnB;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,KAAK,EAAE;IAC1C,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,CAAA;IACtB,IAAI,EAAE,SAAS,CAAC,EAAE,CAAA;IAClB,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,KAAK,MAAM,CAAA;IACtB,OAAO,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,MAAM,CAAA;IAChC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAA;CAC9B,GAAG,IAAI,CAAC,iBAAiB,EAAE,OAAO,GAAG,QAAQ,CAAC,CAc9C"}
@@ -50,6 +50,22 @@ function HoverOverlay({
50
50
  )
51
51
  );
52
52
  }
53
+ function cursorSliderProps(input) {
54
+ const { cursor, data, getX, formatX, ariaLabel } = input;
55
+ const { point } = cursor;
56
+ return {
57
+ onMove: cursor.onPointerMove,
58
+ onDown: cursor.onPointerDown,
59
+ onUp: cursor.onPointerUp,
60
+ onLeave: cursor.onPointerLeave,
61
+ onKeyDown: cursor.onKeyDown,
62
+ onBlur: cursor.onBlur,
63
+ valueMax: Math.max(data.length - 1, 0),
64
+ ...point !== null && { valueNow: data.indexOf(point), valueText: formatX(getX(point)) },
65
+ ...ariaLabel !== void 0 && { ariaLabel }
66
+ };
67
+ }
53
68
  export {
54
- HoverOverlay
69
+ HoverOverlay,
70
+ cursorSliderProps
55
71
  };
@@ -1,3 +1,16 @@
1
+ import type { Dispatch } from 'react';
2
+ /** One frame claiming or releasing the card's header legend slot. */
3
+ export type LegendSlotAction = {
4
+ type: 'claim' | 'release';
5
+ id: string;
6
+ };
7
+ /**
8
+ * The ONE owner of the legend-slot protocol (`docs/waves/PLAN.md` wave 3, P2-9): claimants in claim
9
+ * order, and the head owns the slot. A later claimant is denied simply by not being the head, and
10
+ * it takes over the moment everything ahead of it releases — no retry signal, no ownership mirrored
11
+ * in a ref. Returns the same array on a no-op so a repeated claim/release never re-renders the card.
12
+ */
13
+ export declare function legendSlotReducer(claimants: readonly string[], { type, id }: LegendSlotAction): readonly string[];
1
14
  /**
2
15
  * `ChartCard`'s internal context: `inCard` (a frame inside a card treats its computed height as a
3
16
  * MINIMUM and grows into the card body), the legend slot — the empty header-row box a chart will portal its legend into
@@ -7,23 +20,14 @@
7
20
  * 'dots' before it eats the plot floor (`docs/CHARTS-SPEC.md` §5, wave 11). `null`/`false` until the slot
8
21
  * mounts, and outside any card. Deliberately not exported from any barrel.
9
22
  *
10
- * `claimLegendSlot`/`releaseLegendSlot` arbitrate the slot when a card hosts more than one
11
- * `ChartFrame` (`docs/waves/PLAN.md` wave 3, P2-9): the first frame to claim an id keeps it for as
12
- * long as it holds it, every later claimant is told no and falls back to its own band legend
13
- * instead of a second frame silently portalling into the same node. Outside any card there is no
14
- * slot to contend for, so the default always grants the claim.
15
- *
16
- * `legendSlotVersion` bumps every time the owner releases the slot (round 3's carried gap): a
17
- * denied frame's claim attempt runs once, in its own mount effect, so with no signal that the slot
18
- * freed up it would sit denied forever even after the owning frame unmounts. Frames read it only to
19
- * put it in a dependency array — the effect re-running is the entire point, not the number itself.
23
+ * `legendClaimants` is {@link legendSlotReducer}'s queue (its head owns the slot) and
24
+ * `dispatchLegendSlot` feeds it. Outside any card there is no slot, so nothing ever claims.
20
25
  */
21
26
  export declare const ChartCardContext: import("react").Context<{
22
27
  legendSlot: HTMLDivElement | null;
23
28
  inCard: boolean;
24
29
  short: boolean;
25
- claimLegendSlot: (id: string) => boolean;
26
- releaseLegendSlot: (id: string) => void;
27
- legendSlotVersion: number;
30
+ legendClaimants: readonly string[];
31
+ dispatchLegendSlot: Dispatch<LegendSlotAction>;
28
32
  }>;
29
33
  //# sourceMappingURL=chart-card-context.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"chart-card-context.d.ts","sourceRoot":"","sources":["../../../src/charts/primitives/chart-card-context.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,gBAAgB;gBACf,cAAc,GAAG,IAAI;YACzB,OAAO;WACR,OAAO;qBACG,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO;uBACrB,CAAC,EAAE,EAAE,MAAM,KAAK,IAAI;uBACpB,MAAM;EAQzB,CAAA"}
1
+ {"version":3,"file":"chart-card-context.d.ts","sourceRoot":"","sources":["../../../src/charts/primitives/chart-card-context.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAErC,qEAAqE;AACrE,MAAM,MAAM,gBAAgB,GAAG;IAAE,IAAI,EAAE,OAAO,GAAG,SAAS,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE,CAAA;AAExE;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,SAAS,MAAM,EAAE,EAC5B,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,gBAAgB,GAC7B,SAAS,MAAM,EAAE,CAInB;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,gBAAgB;gBACf,cAAc,GAAG,IAAI;YACzB,OAAO;WACR,OAAO;qBACG,SAAS,MAAM,EAAE;wBACd,QAAQ,CAAC,gBAAgB,CAAC;EAO9C,CAAA"}
@@ -1,13 +1,18 @@
1
1
  import { createContext } from "react";
2
+ function legendSlotReducer(claimants, { type, id }) {
3
+ const held = claimants.includes(id);
4
+ if (type === "claim") return held ? claimants : [...claimants, id];
5
+ return held ? claimants.filter((c) => c !== id) : claimants;
6
+ }
2
7
  const ChartCardContext = createContext({
3
8
  legendSlot: null,
4
9
  inCard: false,
5
10
  short: false,
6
- claimLegendSlot: () => true,
7
- releaseLegendSlot: () => {
8
- },
9
- legendSlotVersion: 0
11
+ legendClaimants: [],
12
+ dispatchLegendSlot: () => {
13
+ }
10
14
  });
11
15
  export {
12
- ChartCardContext
16
+ ChartCardContext,
17
+ legendSlotReducer
13
18
  };
@@ -1 +1 @@
1
- {"version":3,"file":"chart-frame-layout.d.ts","sourceRoot":"","sources":["../../../src/charts/primitives/chart-frame-layout.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"chart-frame-layout.d.ts","sourceRoot":"","sources":["../../../src/charts/primitives/chart-frame-layout.ts"],"names":[],"mappings":"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"}
@@ -97,60 +97,54 @@ const legendEntryWidth = (item) => LEGEND_SWATCH_W + measureText(
97
97
  item.note === void 0 ? item.label : `${item.label} ${item.note}`,
98
98
  VX.legendFontSize
99
99
  );
100
- function fitChipRows(items, width, rows, allowZero = false) {
100
+ function fitGreedyRows(items, fit) {
101
101
  let row = 1;
102
102
  let x = 0;
103
103
  let fitted = 0;
104
- for (const item of items) {
105
- const w = legendEntryWidth(item);
106
- const next = x === 0 ? w : x + VX.legendGap + w;
107
- if (x === 0 && allowZero && next > width) return 0;
108
- if (next > width && x > 0) {
104
+ for (const [i, item] of items.entries()) {
105
+ const w = fit.widthOf(item);
106
+ const next = x === 0 ? w : x + fit.gap + w;
107
+ if (x === 0 && fit.allowZero === true && next > fit.width) return 0;
108
+ if (next > fit.width && x > 0) {
109
109
  row += 1;
110
- if (row > rows) return fitted;
110
+ if (row > fit.rows) return fitted;
111
111
  x = w;
112
112
  } else {
113
113
  x = next;
114
114
  }
115
115
  fitted += 1;
116
+ x += fit.extraAfter?.(i) ?? 0;
116
117
  }
117
118
  return fitted;
118
119
  }
120
+ const chipFit = { widthOf: legendEntryWidth, gap: VX.legendGap };
119
121
  function entriesWithinRows(items, width, rows) {
120
- return fitChipRows(items, width, rows);
122
+ return fitGreedyRows(items, { ...chipFit, width, rows });
121
123
  }
122
124
  function entriesWithinChipRows(items, width, rows) {
123
125
  const total = items.length;
124
- const fittedNoReserve = fitChipRows(items, width, rows);
126
+ const fittedNoReserve = entriesWithinRows(items, width, rows);
125
127
  if (fittedNoReserve >= total) return fittedNoReserve;
126
128
  const reserve = measureText(`All ${total}`, VX.legendFontSize) + VX.legendGap;
127
- return fitChipRows(items, Math.max(width - reserve, 0), rows, true);
128
- }
129
- function fitDotRows(items, width, rows, dividerAfter, allowZero = false) {
130
- let row = 1;
131
- let x = 0;
132
- let fitted = 0;
133
- for (let i = 0; i < items.length; i += 1) {
134
- const next = x === 0 ? LEGEND_DOT_SIZE : x + DOT_PITCH;
135
- if (x === 0 && allowZero && next > width) return 0;
136
- if (next > width && x > 0) {
137
- row += 1;
138
- if (row > rows) return fitted;
139
- x = LEGEND_DOT_SIZE;
140
- } else {
141
- x = next;
142
- }
143
- fitted += 1;
144
- if (dividerAfter.has(i)) x += DOT_PITCH;
145
- }
146
- return fitted;
129
+ return fitGreedyRows(items, {
130
+ ...chipFit,
131
+ width: Math.max(width - reserve, 0),
132
+ rows,
133
+ allowZero: true
134
+ });
147
135
  }
148
136
  function entriesWithinDotRows(items, width, rows, dividerAfter = /* @__PURE__ */ new Set()) {
137
+ const dotFit = {
138
+ widthOf: () => LEGEND_DOT_SIZE,
139
+ gap: DOT_PITCH - LEGEND_DOT_SIZE,
140
+ rows,
141
+ extraAfter: (i) => dividerAfter.has(i) ? DOT_PITCH : 0
142
+ };
149
143
  const total = items.length;
150
- const fittedNoReserve = fitDotRows(items, width, rows, dividerAfter);
144
+ const fittedNoReserve = fitGreedyRows(items, { ...dotFit, width });
151
145
  if (fittedNoReserve >= total) return fittedNoReserve;
152
146
  const reserve = measureText(`All ${total}`, VX.legendFontSize) + DOT_PITCH;
153
- return fitDotRows(items, Math.max(width - reserve, 0), rows, dividerAfter, true);
147
+ return fitGreedyRows(items, { ...dotFit, width: Math.max(width - reserve, 0), allowZero: true });
154
148
  }
155
149
  function legendEntryCap(input) {
156
150
  const { items, containerW, available, defaultMaxRows } = input;
@@ -0,0 +1,27 @@
1
+ import type { CSSProperties, ReactNode } from 'react';
2
+ /**
3
+ * `ChartLegend`'s `All N` disclosure state: which button opened it (the chip, or a dots-mode entry)
4
+ * and where the fixed panel sits. Focus moves into the panel on open and back to that trigger on
5
+ * EVERY close path; a scroll outside the panel or a resize closes it, since it is fixed to where the
6
+ * trigger WAS.
7
+ */
8
+ export declare function useLegendDisclosure(coarse: boolean): {
9
+ open: boolean;
10
+ panelId: string;
11
+ openFrom: (trigger: HTMLButtonElement) => void;
12
+ close: () => void;
13
+ panel: {
14
+ panelRef: import("react").RefObject<HTMLDialogElement | null>;
15
+ panelId: string;
16
+ coarse: boolean;
17
+ placement: CSSProperties | null;
18
+ close: () => void;
19
+ };
20
+ };
21
+ /** The portalled panel itself — a popover under the chip on a fine pointer, a docked sheet (over a
22
+ * scrim) on a coarse one. Fixed-position, so opening it never grows the legend band. */
23
+ export declare function LegendDisclosure({ panelRef, panelId, coarse, placement, close, fontSize, children, }: ReturnType<typeof useLegendDisclosure>['panel'] & {
24
+ fontSize: number;
25
+ children: ReactNode;
26
+ }): import("react").ReactPortal | null;
27
+ //# sourceMappingURL=legend-disclosure.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"legend-disclosure.d.ts","sourceRoot":"","sources":["../../../src/charts/primitives/legend-disclosure.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAwDrD;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,OAAO;;;wBAQtB,iBAAiB;;;;;;;;;EAuD7C;AAED;wFACwF;AACxF,wBAAgB,gBAAgB,CAAC,EAC/B,QAAQ,EACR,OAAO,EACP,MAAM,EACN,SAAS,EACT,KAAK,EACL,QAAQ,EACR,QAAQ,GACT,EAAE,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC,OAAO,CAAC,GAAG;IACnD,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,SAAS,CAAA;CACpB,sCAoCA"}