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.
Files changed (91) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/MIGRATING.md +101 -17
  3. package/agent/rules/basalt-mantine.md +4 -4
  4. package/configs/oxlint-plugin.js +159 -42
  5. package/configs/oxlint-plugin.test.ts +182 -0
  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 +33 -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.module.css +3 -13
  48. package/dist/guard/index.d.ts.map +1 -1
  49. package/dist/guard/index.js +6 -5
  50. package/dist/notifications/center.d.ts.map +1 -1
  51. package/dist/provider/index.d.ts.map +1 -1
  52. package/dist/provider/index.js +12 -10
  53. package/dist/shell/aside-docking.d.ts +31 -0
  54. package/dist/shell/aside-docking.d.ts.map +1 -0
  55. package/dist/shell/aside-docking.js +19 -0
  56. package/dist/shell/index.d.ts +0 -11
  57. package/dist/shell/index.d.ts.map +1 -1
  58. package/dist/shell/index.js +13 -14
  59. package/dist/shell/use-breakpoint.d.ts +1 -1
  60. package/dist/theme/floating.module.css +1 -0
  61. package/dist/theme-lab/derive-controls.d.ts.map +1 -1
  62. package/llms.txt +2 -2
  63. package/package.json +2 -1
  64. package/src/charts/kinds/BandStrip.tsx +18 -64
  65. package/src/charts/kinds/DualPanel.tsx +4 -24
  66. package/src/charts/kinds/MirroredBars.tsx +12 -66
  67. package/src/charts/kinds/band-chrome.tsx +91 -0
  68. package/src/charts/primitives/CartesianChart.tsx +79 -122
  69. package/src/charts/primitives/ChartCard.tsx +5 -27
  70. package/src/charts/primitives/ChartFrame.tsx +37 -76
  71. package/src/charts/primitives/ChartLegend.tsx +7 -154
  72. package/src/charts/primitives/ChartTooltip.tsx +16 -0
  73. package/src/charts/primitives/HoverOverlay.tsx +52 -21
  74. package/src/charts/primitives/chart-card-context.ts +25 -16
  75. package/src/charts/primitives/chart-frame-layout.ts +44 -56
  76. package/src/charts/primitives/legend-disclosure.tsx +177 -0
  77. package/src/controls/controls.module.css +5 -0
  78. package/src/controls/sync-button.module.css +1 -0
  79. package/src/dashboard/stat-group.module.css +19 -64
  80. package/src/dashboard/widget-grid.module.css +11 -38
  81. package/src/data/data-table-state.ts +218 -0
  82. package/src/data/data-table.tsx +127 -218
  83. package/src/forms/form-layout.module.css +3 -13
  84. package/src/guard/index.ts +21 -16
  85. package/src/notifications/center.tsx +3 -0
  86. package/src/provider/index.tsx +18 -17
  87. package/src/shell/aside-docking.ts +54 -0
  88. package/src/shell/index.tsx +15 -37
  89. package/src/shell/use-breakpoint.ts +2 -2
  90. package/src/theme/floating.module.css +1 -0
  91. package/src/theme-lab/derive-controls.tsx +2 -0
@@ -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":"AAAA;;;GAGG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAA;AAC/D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAQhD,uGAAuG;AACvG,eAAO,MAAM,cAAc,IAAI,CAAA;AAI/B;;;;;;;;;GASG;AACH,eAAO,MAAM,eAAe,IAAI,CAAA;AAChC,eAAO,MAAM,YAAY,QAAuC,CAAA;AAShE;;;;GAIG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,SAAS,WAAW,EAAE,EAC7B,MAAM,EAAE,OAAO,GACd;IAAE,OAAO,EAAE,WAAW,EAAE,CAAC;IAAC,YAAY,EAAE,WAAW,CAAC,MAAM,CAAC,CAAA;CAAE,CAa/D;AAED;;;;GAIG;AACH,MAAM,MAAM,SAAS,GAAG,OAAO,GAAG,SAAS,CAAA;AAE3C;;;;;;;;;;GAUG;AACH,MAAM,MAAM,qBAAqB,GAAG;IAClC,IAAI,EAAE,MAAM,CAAA;IACZ,qDAAqD;IACrD,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,kDAAkD;IAClD,IAAI,CAAC,EAAE,MAAM,CAAA;CACd,CAAA;AAED;;;;;GAKG;AACH,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,MAAM,GAAG,qBAAqB,EACtC,cAAc,EAAE,cAAc,GAC7B,MAAM,CAKR;AAgBD;;;;GAIG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,2FAA2F;IAC3F,IAAI,EAAE,SAAS,CAAA;IACf;uFACmF;IACnF,QAAQ,EAAE,MAAM,CAAA;IAChB,kCAAkC;IAClC,cAAc,EAAE,MAAM,CAAA;IACtB,gCAAgC;IAChC,IAAI,EAAE,MAAM,CAAA;IACZ,uCAAuC;IACvC,eAAe,EAAE,MAAM,CAAA;IACvB,6EAA6E;IAC7E,MAAM,EAAE,WAAW,CAAA;CACpB,CAAA;AA2BD,8FAA8F;AAC9F,wBAAgB,YAAY,CAAC,cAAc,EAAE,cAAc,GAAG,gBAAgB,CAI7E;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE;IACzC,yCAAyC;IACzC,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAClC,iFAAiF;IACjF,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAA;IAClC,2FAA2F;IAC3F,QAAQ,EAAE,OAAO,CAAA;IACjB,KAAK,EAAE,SAAS,WAAW,EAAE,CAAA;IAC7B,UAAU,EAAE,MAAM,CAAA;IAClB,iFAAiF;IACjF,SAAS,EAAE,MAAM,CAAA;CAClB,GAAG;IAAE,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,UAAU,EAAE,OAAO,CAAA;CAAE,CAavD;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,eAAe,CAAC,KAAK,EAAE;IACrC,UAAU,EAAE,MAAM,CAAA;IAClB,cAAc,EAAE,MAAM,CAAA;IACtB,QAAQ,EAAE,MAAM,CAAA;IAChB,eAAe,EAAE,MAAM,CAAA;IACvB,qBAAqB,EAAE,MAAM,CAAA;IAC7B;;;;;;;OAOG;IACH,UAAU,CAAC,EAAE,OAAO,CAAA;CACrB,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAepC;AAkED;kFACkF;AAClF,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,SAAS,WAAW,EAAE,EAC7B,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,GACX,MAAM,CAER;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,SAAS,WAAW,EAAE,EAC7B,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,GACX,MAAM,CAMR;AAoCD;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,SAAS,WAAW,EAAE,EAC7B,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,YAAY,GAAE,WAAW,CAAC,MAAM,CAAa,GAC5C,MAAM,CAMR;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE;IACpC,KAAK,EAAE,SAAS,WAAW,EAAE,CAAA;IAC7B,UAAU,EAAE,MAAM,CAAA;IAClB,iFAAiF;IACjF,SAAS,EAAE,MAAM,CAAA;IACjB;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB,GAAG,MAAM,GAAG,SAAS,CAerB"}
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"}
@@ -0,0 +1,153 @@
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
+ import { alpha, VX } from "../../tokens/index.js";
5
+ import { useDismissOnOutside } from "../cursor/touch-pin.js";
6
+ import { LEGEND_ROW_GAP } from "./chart-frame-layout.js";
7
+ const DISCLOSURE_PANEL = {
8
+ position: "fixed",
9
+ zIndex: VX.zIndexFloating,
10
+ display: "flex",
11
+ flexDirection: "column",
12
+ gap: LEGEND_ROW_GAP,
13
+ // theme-allow raw-scroll-container — a fixed floating panel of one legend, not app chrome
14
+ overflowY: "auto",
15
+ // `<dialog open>` brings UA chrome (centring margin, border, fit-content box) this resets.
16
+ margin: 0,
17
+ border: "none",
18
+ width: "auto",
19
+ height: "auto",
20
+ padding: 10,
21
+ boxSizing: "border-box",
22
+ backgroundColor: VX.surface.overlay,
23
+ boxShadow: VX.shadowOverlay,
24
+ color: VX.muted,
25
+ borderRadius: VX.radiusCard
26
+ };
27
+ const DISCLOSURE_MIN_HEIGHT = 96;
28
+ function disclosurePlacement(chip, coarse) {
29
+ if (coarse) {
30
+ return {
31
+ left: 0,
32
+ right: 0,
33
+ bottom: 0,
34
+ maxHeight: "60dvh",
35
+ borderBottomLeftRadius: 0,
36
+ borderBottomRightRadius: 0,
37
+ padding: "12px 16px calc(12px + env(safe-area-inset-bottom, 0px))"
38
+ };
39
+ }
40
+ const below = window.innerHeight - chip.bottom;
41
+ const left = Math.max(8, Math.min(chip.left, window.innerWidth - 248));
42
+ const base = { left, right: "auto", minWidth: 200, maxWidth: "calc(100vw - 16px)" };
43
+ return below >= 200 || below >= chip.top ? { ...base, top: chip.bottom + 4, maxHeight: Math.max(below - 12, DISCLOSURE_MIN_HEIGHT) } : {
44
+ ...base,
45
+ bottom: window.innerHeight - chip.top + 4,
46
+ maxHeight: Math.max(chip.top - 12, DISCLOSURE_MIN_HEIGHT)
47
+ };
48
+ }
49
+ function useLegendDisclosure(coarse) {
50
+ const [placement, setPlacement] = useState(null);
51
+ const triggerRef = useRef(null);
52
+ const panelRef = useRef(null);
53
+ const panelId = useId();
54
+ const wasOpen = useRef(false);
55
+ const open = placement !== null;
56
+ const close = useCallback(() => setPlacement(null), []);
57
+ const openFrom = (trigger) => {
58
+ triggerRef.current = trigger;
59
+ setPlacement(disclosurePlacement(trigger.getBoundingClientRect(), coarse));
60
+ };
61
+ useEffect(() => {
62
+ if (open) panelRef.current?.focus();
63
+ else if (wasOpen.current) triggerRef.current?.focus();
64
+ wasOpen.current = open;
65
+ }, [open]);
66
+ const isInsidePanel = useCallback(
67
+ (target) => panelRef.current?.contains(target) === true || triggerRef.current?.contains(target) === true,
68
+ []
69
+ );
70
+ useDismissOnOutside({
71
+ enabled: open,
72
+ isInside: isInsidePanel,
73
+ onDismiss: close,
74
+ dismissOnResize: true,
75
+ scrollExemptInside: true
76
+ });
77
+ useEffect(() => {
78
+ if (!open || !coarse) return;
79
+ const panel = panelRef.current;
80
+ if (panel === null) return;
81
+ const onKeyDown = (e) => {
82
+ if (e.key !== "Tab") return;
83
+ const focusable = panel.querySelectorAll(
84
+ 'button, [tabindex]:not([tabindex="-1"])'
85
+ );
86
+ const first = focusable[0];
87
+ const last = focusable[focusable.length - 1];
88
+ if (first === void 0 || last === void 0) return;
89
+ if (e.shiftKey && document.activeElement === first) {
90
+ e.preventDefault();
91
+ last.focus();
92
+ } else if (!e.shiftKey && document.activeElement === last) {
93
+ e.preventDefault();
94
+ first.focus();
95
+ }
96
+ };
97
+ panel.addEventListener("keydown", onKeyDown);
98
+ return () => panel.removeEventListener("keydown", onKeyDown);
99
+ }, [open, coarse]);
100
+ return { open, panelId, openFrom, close, panel: { panelRef, panelId, coarse, placement, close } };
101
+ }
102
+ function LegendDisclosure({
103
+ panelRef,
104
+ panelId,
105
+ coarse,
106
+ placement,
107
+ close,
108
+ fontSize,
109
+ children
110
+ }) {
111
+ if (placement === null) return null;
112
+ return createPortal(
113
+ /* @__PURE__ */ jsxs(Fragment, { children: [
114
+ coarse && /* @__PURE__ */ jsx(
115
+ "div",
116
+ {
117
+ "aria-hidden": true,
118
+ onPointerDown: (e) => {
119
+ e.stopPropagation();
120
+ close();
121
+ },
122
+ style: {
123
+ position: "fixed",
124
+ inset: 0,
125
+ // One below the panel it sits behind — still `VX.zIndexFloating`-derived, never a
126
+ // second unrelated literal (B14).
127
+ zIndex: VX.zIndexFloating - 1,
128
+ backgroundColor: alpha(VX.neutral, 0.35)
129
+ }
130
+ }
131
+ ),
132
+ /* @__PURE__ */ jsx(
133
+ "dialog",
134
+ {
135
+ ref: panelRef,
136
+ id: panelId,
137
+ open: true,
138
+ tabIndex: -1,
139
+ "aria-label": "Legend",
140
+ ...coarse && { "aria-modal": true },
141
+ "data-basalt-legend-disclosure": coarse ? "sheet" : "popover",
142
+ style: { ...DISCLOSURE_PANEL, fontSize, ...placement },
143
+ children
144
+ }
145
+ )
146
+ ] }),
147
+ document.body
148
+ );
149
+ }
150
+ export {
151
+ LegendDisclosure,
152
+ useLegendDisclosure
153
+ };
@@ -49,6 +49,11 @@
49
49
  flex-shrink: 0;
50
50
  }
51
51
 
52
+ /* theme-allow-file raw-media-query — the ControlRow's own C9 swap (pills inline, the rest in a
53
+ sheet) keys on the SIZE CLASS by design: the sheet is compact-class chrome, the same axis the
54
+ shell's bottom bar reads. Every width query here is `SIZE_CLASSES.medium`, pinned by
55
+ `breakpoint-literals.test.ts`. */
56
+
52
57
  /* Below `sm` only the first `inline` slots are pills; the rest live in the sheet. */
53
58
  @media (max-width: 52.49375em) {
54
59
  .pillSlot:not([data-inline]) {
@@ -45,6 +45,7 @@
45
45
  * width itself, pinned to the `ctl` height so the box is square. This module is unlayered, so it
46
46
  * outranks `@layer mantine` regardless of specificity.
47
47
  */
48
+ /* theme-allow raw-media-query — the control's own C9 icon-only swap at the compact size class */
48
49
  @media (max-width: 52.49375em) {
49
50
  .label,
50
51
  .age {
@@ -15,24 +15,24 @@
15
15
  * one: a query with no container resolves to false, so a shell-only context would have silently
16
16
  * pinned every non-shell consumer to the base count.
17
17
  *
18
- * The `@supports not` block below it is the pre-2023 fallback and the ONLY place the old viewport
19
- * law survives. It is written as an exclusive alternative rather than as a base the container
20
- * queries override, because the rail's `:nth-child` suppressors are MORE specific than the rule
21
- * that draws the border: two overlapping laws would each suppress a different set of leading cells
22
- * and the rail would lose hairlines in the middle of a row. One law at a time, chosen by support.
18
+ * The literals are the container-class law (`CONTAINER_CLASSES`, `tokens/size-classes.ts`): the
19
+ * `sm` count opens at `wide` (800px), not at the viewport-era 768. Two are StatGroup's own and say
20
+ * so: 260px (one column, below) and 1200px, where the full `cols` count opens. The class table
21
+ * stops at 800, and 800 is too early for a full KPI row — four cells of ~190px clamp a value a real
22
+ * dashboard prints (`tests/layout/page-aside.layout.test.ts` measures ~195px against the 844px row
23
+ * a claimed aside leaves). Conditions are px, never `em`: `em` inside a `@container` condition
24
+ * resolves against the CONTAINER's font-size, so a consumer panel with smaller text would silently
25
+ * move the boundary. A condition cannot read a custom property, so the numbers are written; the
26
+ * COUNTS are custom properties the component sets per instance.
23
27
  *
24
- * The literals: `@container` conditions are stated in px, `@media` ones in Mantine's own `em`
25
- * (`--mantine-breakpoint-sm` 48em, `--mantine-breakpoint-lg` 75em). Both spellings name the same
26
- * boundary at the 16px initial font size — 768px and 1200px — but `em` inside a `@container`
27
- * condition resolves against the CONTAINER's font-size rather than the root's, so a consumer panel
28
- * with smaller text would silently move the breakpoint. px is the only spelling that cannot drift.
29
- * (`47.99375em` → `767.9px`, `74.99375em` → `1199.9px`.) A condition still cannot read a custom property either way, so the numbers are written;
30
- * the COUNTS are custom properties the component sets per instance.
28
+ * No viewport fallback: basalt's own CSS already requires `color-mix()` (Safari 16.2, Chrome 111,
29
+ * Firefox 113), and every one of those parses `@container` — a `@supports not (container-type)`
30
+ * block is unreachable in any browser that renders basalt at all.
31
31
  */
32
32
 
33
33
  .container {
34
34
  container-type: inline-size;
35
- container-name: basalt-stat-group;
35
+ container-name: basalt-grid;
36
36
  }
37
37
 
38
38
  .root {
@@ -45,7 +45,7 @@
45
45
 
46
46
  /* 260px is StatGroup's own literal (docs/DESIGN-CORE.md § Layout, elevation, shapes), not a container class: one column below it,
47
47
  * so every cell leads a row and the rail draws no leading hairline at all. */
48
- @container basalt-stat-group (max-width: 259.9px) {
48
+ @container basalt-grid (max-width: 259.9px) {
49
49
  .root {
50
50
  grid-template-columns: minmax(0, 1fr);
51
51
  }
@@ -56,13 +56,13 @@
56
56
  }
57
57
  }
58
58
 
59
- @container basalt-stat-group (min-width: 768px) {
59
+ @container basalt-grid (min-width: 800px) {
60
60
  .root {
61
61
  grid-template-columns: repeat(var(--basalt-stat-group-cols-sm), minmax(0, 1fr));
62
62
  }
63
63
  }
64
64
 
65
- @container basalt-stat-group (min-width: 1200px) {
65
+ @container basalt-grid (min-width: 1200px) {
66
66
  .root {
67
67
  grid-template-columns: repeat(var(--basalt-stat-group-cols-lg), minmax(0, 1fr));
68
68
  }
@@ -83,14 +83,14 @@
83
83
  padding-inline-start: var(--vx-space-stack-md);
84
84
  }
85
85
 
86
- @container basalt-stat-group (max-width: 767.9px) {
86
+ @container basalt-grid (max-width: 799.9px) {
87
87
  .root[data-divided] > *:nth-child(2n + 1) {
88
88
  border-inline-start: 0;
89
89
  padding-inline-start: 0;
90
90
  }
91
91
  }
92
92
 
93
- @container basalt-stat-group (min-width: 768px) and (max-width: 1199.9px) {
93
+ @container basalt-grid (min-width: 800px) and (max-width: 1199.9px) {
94
94
  .root[data-divided][data-sm-cols='2'] > *:nth-child(2n + 1),
95
95
  .root[data-divided][data-sm-cols='3'] > *:nth-child(3n + 1) {
96
96
  border-inline-start: 0;
@@ -98,7 +98,7 @@
98
98
  }
99
99
  }
100
100
 
101
- @container basalt-stat-group (min-width: 1200px) {
101
+ @container basalt-grid (min-width: 1200px) {
102
102
  .root[data-divided][data-cols='2'] > *:nth-child(2n + 1),
103
103
  .root[data-divided][data-cols='3'] > *:nth-child(3n + 1),
104
104
  .root[data-divided][data-cols='4'] > *:nth-child(4n + 1),
@@ -107,48 +107,3 @@
107
107
  padding-inline-start: 0;
108
108
  }
109
109
  }
110
-
111
- /*
112
- * The pre-container-query fallback — Safari < 16, Chrome < 106, Firefox < 110. Verbatim the law
113
- * this file shipped before, keyed on the viewport, and reachable ONLY where `@container` is not.
114
- * A browser that cannot parse `container-type` also cannot parse the `@container` blocks above, so
115
- * the two sets are never live at once.
116
- */
117
- @supports not (container-type: inline-size) {
118
- @media (min-width: 48em) {
119
- .root {
120
- grid-template-columns: repeat(var(--basalt-stat-group-cols-sm), minmax(0, 1fr));
121
- }
122
- }
123
-
124
- @media (min-width: 75em) {
125
- .root {
126
- grid-template-columns: repeat(var(--basalt-stat-group-cols-lg), minmax(0, 1fr));
127
- }
128
- }
129
-
130
- @media (max-width: 47.99375em) {
131
- .root[data-divided] > *:nth-child(2n + 1) {
132
- border-inline-start: 0;
133
- padding-inline-start: 0;
134
- }
135
- }
136
-
137
- @media (min-width: 48em) and (max-width: 74.99375em) {
138
- .root[data-divided][data-sm-cols='2'] > *:nth-child(2n + 1),
139
- .root[data-divided][data-sm-cols='3'] > *:nth-child(3n + 1) {
140
- border-inline-start: 0;
141
- padding-inline-start: 0;
142
- }
143
- }
144
-
145
- @media (min-width: 75em) {
146
- .root[data-divided][data-cols='2'] > *:nth-child(2n + 1),
147
- .root[data-divided][data-cols='3'] > *:nth-child(3n + 1),
148
- .root[data-divided][data-cols='4'] > *:nth-child(4n + 1),
149
- .root[data-divided][data-cols='5'] > *:nth-child(5n + 1) {
150
- border-inline-start: 0;
151
- padding-inline-start: 0;
152
- }
153
- }
154
- }
@@ -15,18 +15,19 @@
15
15
  * container. A span resolved against the viewport while the tracks follow the container is exactly
16
16
  * the phantom-track defect `WidgetGrid.Item`'s context clamp exists to prevent.
17
17
  *
18
- * The literals: px in `@container`, Mantine's own `em` in the `@supports not` fallback — same
19
- * boundary (768px / 1200px at the 16px initial font size), but `em` inside a `@container`
20
- * condition resolves against the CONTAINER's font-size, so px is the only spelling that cannot
21
- * drift. A condition cannot read a custom property either way, so the numbers are written; the
22
- * COUNTS are custom properties, set once per instance by the component — three hand-written
23
- * `repeat()` blocks would have to enumerate every `cols` value, and the whole point of this
24
- * primitive is that a consumer states one desktop number and basalt owns the rest.
18
+ * The literals are the container-class law, as in `stat-group.module.css`: the `sm` count opens
19
+ * at `wide` (800px); 1200px, where the full `cols` count opens, is the grid's own boundary above
20
+ * the class table's top step. px, never `em` (an `em` condition resolves against the container's
21
+ * font-size). A condition cannot read a custom property, so the numbers are written; the COUNTS
22
+ * are custom properties, set once per instance by the component — three hand-written `repeat()`
23
+ * blocks would have to enumerate every `cols` value, and the whole point of this primitive is that
24
+ * a consumer states one desktop number and basalt owns the rest. No viewport fallback, for the
25
+ * reason `stat-group.module.css` states: nothing that renders basalt lacks `@container`.
25
26
  */
26
27
 
27
28
  .container {
28
29
  container-type: inline-size;
29
- container-name: basalt-widget-grid;
30
+ container-name: basalt-grid;
30
31
  }
31
32
 
32
33
  .root {
@@ -47,7 +48,7 @@
47
48
  min-width: 0;
48
49
  }
49
50
 
50
- @container basalt-widget-grid (min-width: 768px) {
51
+ @container basalt-grid (min-width: 800px) {
51
52
  .root {
52
53
  grid-template-columns: repeat(var(--basalt-widget-grid-cols-sm), minmax(0, 1fr));
53
54
  }
@@ -57,7 +58,7 @@
57
58
  }
58
59
  }
59
60
 
60
- @container basalt-widget-grid (min-width: 1200px) {
61
+ @container basalt-grid (min-width: 1200px) {
61
62
  .root {
62
63
  grid-template-columns: repeat(var(--basalt-widget-grid-cols-lg), minmax(0, 1fr));
63
64
  }
@@ -66,31 +67,3 @@
66
67
  grid-column: span var(--basalt-widget-grid-span-lg);
67
68
  }
68
69
  }
69
-
70
- /*
71
- * The pre-container-query fallback — Safari < 16, Chrome < 106, Firefox < 110. Verbatim the law
72
- * this file shipped before, keyed on the viewport, and reachable ONLY where `@container` is not: a
73
- * browser that cannot parse `container-type` cannot parse the blocks above either, so the two sets
74
- * are never live at once.
75
- */
76
- @supports not (container-type: inline-size) {
77
- @media (min-width: 48em) {
78
- .root {
79
- grid-template-columns: repeat(var(--basalt-widget-grid-cols-sm), minmax(0, 1fr));
80
- }
81
-
82
- .item {
83
- grid-column: span var(--basalt-widget-grid-span-sm);
84
- }
85
- }
86
-
87
- @media (min-width: 75em) {
88
- .root {
89
- grid-template-columns: repeat(var(--basalt-widget-grid-cols-lg), minmax(0, 1fr));
90
- }
91
-
92
- .item {
93
- grid-column: span var(--basalt-widget-grid-span-lg);
94
- }
95
- }
96
- }