basalt-ui 1.30.2 → 1.31.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 (334) hide show
  1. package/AGENTS.md +1 -1
  2. package/CHANGELOG.md +31 -0
  3. package/MIGRATING.md +172 -12
  4. package/agent/rules/basalt-controls.md +19 -19
  5. package/agent/rules/basalt-mantine.md +1 -1
  6. package/configs/oxlint-plugin.js +254 -4
  7. package/configs/oxlint-plugin.test.ts +145 -2
  8. package/configs/oxlint.json +2 -1
  9. package/dist/agent-chat/composer.d.ts.map +1 -1
  10. package/dist/agent-chat/composer.js +41 -43
  11. package/dist/agent-chat/thread-workspace.d.ts +2 -2
  12. package/dist/agent-chat/thread-workspace.d.ts.map +1 -1
  13. package/dist/agent-chat/thread-workspace.js +3 -4
  14. package/dist/agent-chat/use-keyboard-inset.d.ts +2 -0
  15. package/dist/agent-chat/use-keyboard-inset.d.ts.map +1 -0
  16. package/dist/agent-chat/use-keyboard-inset.js +34 -0
  17. package/dist/charts/cursor/touch-pin.d.ts +45 -0
  18. package/dist/charts/cursor/touch-pin.d.ts.map +1 -0
  19. package/dist/charts/cursor/touch-pin.js +79 -0
  20. package/dist/charts/hooks/use-discrete-cursor.d.ts +66 -0
  21. package/dist/charts/hooks/use-discrete-cursor.d.ts.map +1 -0
  22. package/dist/charts/hooks/use-discrete-cursor.js +198 -0
  23. package/dist/charts/hooks/use-hysteresis.d.ts +18 -0
  24. package/dist/charts/hooks/use-hysteresis.d.ts.map +1 -0
  25. package/dist/charts/hooks/use-hysteresis.js +12 -0
  26. package/dist/charts/hooks/useBandPlot.d.ts +7 -0
  27. package/dist/charts/hooks/useBandPlot.d.ts.map +1 -1
  28. package/dist/charts/hooks/useBandPlot.js +62 -18
  29. package/dist/charts/hooks/useChartCursor.d.ts +40 -11
  30. package/dist/charts/hooks/useChartCursor.d.ts.map +1 -1
  31. package/dist/charts/hooks/useChartCursor.js +102 -11
  32. package/dist/charts/index.d.ts +0 -1
  33. package/dist/charts/index.d.ts.map +1 -1
  34. package/dist/charts/index.js +0 -10
  35. package/dist/charts/kinds/BandStrip.d.ts.map +1 -1
  36. package/dist/charts/kinds/BandStrip.js +54 -41
  37. package/dist/charts/kinds/Donut.d.ts +9 -3
  38. package/dist/charts/kinds/Donut.d.ts.map +1 -1
  39. package/dist/charts/kinds/Donut.js +110 -48
  40. package/dist/charts/kinds/DualPanel.d.ts.map +1 -1
  41. package/dist/charts/kinds/DualPanel.js +145 -131
  42. package/dist/charts/kinds/Heatmap.d.ts +7 -0
  43. package/dist/charts/kinds/Heatmap.d.ts.map +1 -1
  44. package/dist/charts/kinds/Heatmap.js +37 -16
  45. package/dist/charts/kinds/MirroredBars.d.ts.map +1 -1
  46. package/dist/charts/kinds/MirroredBars.js +83 -68
  47. package/dist/charts/kinds/donut-layout.d.ts +47 -0
  48. package/dist/charts/kinds/donut-layout.d.ts.map +1 -0
  49. package/dist/charts/kinds/donut-layout.js +52 -0
  50. package/dist/charts/layout/auto-margin.d.ts +7 -2
  51. package/dist/charts/layout/auto-margin.d.ts.map +1 -1
  52. package/dist/charts/layout/auto-margin.js +12 -3
  53. package/dist/charts/layout/end-labels.d.ts +64 -0
  54. package/dist/charts/layout/end-labels.d.ts.map +1 -0
  55. package/dist/charts/layout/end-labels.js +76 -0
  56. package/dist/charts/primitives/Axes.d.ts +32 -4
  57. package/dist/charts/primitives/Axes.d.ts.map +1 -1
  58. package/dist/charts/primitives/Axes.js +62 -16
  59. package/dist/charts/primitives/CartesianChart.d.ts +7 -6
  60. package/dist/charts/primitives/CartesianChart.d.ts.map +1 -1
  61. package/dist/charts/primitives/CartesianChart.js +271 -125
  62. package/dist/charts/primitives/ChartCard.d.ts +0 -14
  63. package/dist/charts/primitives/ChartCard.d.ts.map +1 -1
  64. package/dist/charts/primitives/ChartCard.js +74 -21
  65. package/dist/charts/primitives/ChartFrame.d.ts +28 -22
  66. package/dist/charts/primitives/ChartFrame.d.ts.map +1 -1
  67. package/dist/charts/primitives/ChartFrame.js +133 -34
  68. package/dist/charts/primitives/ChartLegend.d.ts +19 -15
  69. package/dist/charts/primitives/ChartLegend.d.ts.map +1 -1
  70. package/dist/charts/primitives/ChartLegend.js +212 -71
  71. package/dist/charts/primitives/ChartTooltip.d.ts.map +1 -1
  72. package/dist/charts/primitives/ChartTooltip.js +4 -3
  73. package/dist/charts/primitives/Crosshair.js +2 -2
  74. package/dist/charts/primitives/HoverOverlay.d.ts +6 -1
  75. package/dist/charts/primitives/HoverOverlay.d.ts.map +1 -1
  76. package/dist/charts/primitives/HoverOverlay.js +37 -26
  77. package/dist/charts/primitives/HoverOverlay.module.css +13 -0
  78. package/dist/charts/primitives/HoverOverlay.module.css.d.ts +8 -0
  79. package/dist/charts/primitives/chart-card-context.d.ts +29 -0
  80. package/dist/charts/primitives/chart-card-context.d.ts.map +1 -0
  81. package/dist/charts/primitives/chart-card-context.js +13 -0
  82. package/dist/charts/primitives/chart-frame-layout.d.ts +93 -113
  83. package/dist/charts/primitives/chart-frame-layout.d.ts.map +1 -1
  84. package/dist/charts/primitives/chart-frame-layout.js +76 -29
  85. package/dist/charts/primitives/chart-layout.d.ts +112 -0
  86. package/dist/charts/primitives/chart-layout.d.ts.map +1 -0
  87. package/dist/charts/primitives/chart-layout.js +175 -0
  88. package/dist/charts/primitives/chart-tier.d.ts +13 -11
  89. package/dist/charts/primitives/chart-tier.d.ts.map +1 -1
  90. package/dist/charts/primitives/chart-tier.js +42 -13
  91. package/dist/charts/series.d.ts +4 -1
  92. package/dist/charts/series.d.ts.map +1 -1
  93. package/dist/charts/utils/ticks.d.ts +27 -8
  94. package/dist/charts/utils/ticks.d.ts.map +1 -1
  95. package/dist/charts/utils/ticks.js +35 -11
  96. package/dist/common/errors.d.ts +11 -1
  97. package/dist/common/errors.d.ts.map +1 -1
  98. package/dist/common/errors.js +16 -2
  99. package/dist/common/hit-floor.d.ts +10 -0
  100. package/dist/common/hit-floor.d.ts.map +1 -0
  101. package/dist/common/hit-floor.js +6 -0
  102. package/dist/{shell → common}/isomorphic-layout-effect.d.ts +4 -3
  103. package/dist/common/isomorphic-layout-effect.d.ts.map +1 -0
  104. package/dist/common/use-measured-widths.d.ts +37 -0
  105. package/dist/common/use-measured-widths.d.ts.map +1 -0
  106. package/dist/common/use-measured-widths.js +51 -0
  107. package/dist/common/use-media-query.d.ts +6 -0
  108. package/dist/common/use-media-query.d.ts.map +1 -0
  109. package/dist/common/use-media-query.js +40 -0
  110. package/dist/common/validate.d.ts +9 -0
  111. package/dist/common/validate.d.ts.map +1 -1
  112. package/dist/common/validate.js +9 -4
  113. package/dist/content/article-layout.d.ts +4 -2
  114. package/dist/content/article-layout.d.ts.map +1 -1
  115. package/dist/content/article-layout.js +43 -3
  116. package/dist/content/article-layout.module.css +45 -4
  117. package/dist/content/article-layout.module.css.d.ts +3 -0
  118. package/dist/content/code-block.d.ts.map +1 -1
  119. package/dist/content/code-block.js +40 -2
  120. package/dist/content/code-block.module.css +32 -2
  121. package/dist/content/prose.module.css +13 -8
  122. package/dist/controls/actions.d.ts +29 -6
  123. package/dist/controls/actions.d.ts.map +1 -1
  124. package/dist/controls/actions.js +158 -27
  125. package/dist/controls/actions.module.css +14 -1
  126. package/dist/controls/actions.module.css.d.ts +1 -0
  127. package/dist/controls/controls.module.css +8 -31
  128. package/dist/controls/enum-filter.js +2 -2
  129. package/dist/controls/filter-context.d.ts +10 -0
  130. package/dist/controls/filter-context.d.ts.map +1 -1
  131. package/dist/controls/filter-context.js +2 -0
  132. package/dist/controls/filter-pill.d.ts.map +1 -1
  133. package/dist/controls/filter-pill.js +1 -0
  134. package/dist/controls/index.d.ts +1 -1
  135. package/dist/controls/index.d.ts.map +1 -1
  136. package/dist/controls/index.js +0 -2
  137. package/dist/controls/multi-select-filter.js +2 -2
  138. package/dist/controls/sync-button.module.css +1 -1
  139. package/dist/dashboard/section.d.ts.map +1 -1
  140. package/dist/dashboard/section.js +1 -0
  141. package/dist/dashboard/section.module.css +1 -26
  142. package/dist/dashboard/stat-card.d.ts +1 -1
  143. package/dist/dashboard/stat-card.d.ts.map +1 -1
  144. package/dist/dashboard/stat-card.js +4 -1
  145. package/dist/dashboard/stat-card.module.css +6 -9
  146. package/dist/dashboard/stat-group.module.css +16 -4
  147. package/dist/dashboard/widget-grid.d.ts.map +1 -1
  148. package/dist/dashboard/widget-header.d.ts +14 -4
  149. package/dist/dashboard/widget-header.d.ts.map +1 -1
  150. package/dist/dashboard/widget-header.js +23 -6
  151. package/dist/dashboard/widget-header.module.css +145 -133
  152. package/dist/dashboard/widget-header.module.css.d.ts +2 -0
  153. package/dist/data/data-table.d.ts +23 -0
  154. package/dist/data/data-table.d.ts.map +1 -1
  155. package/dist/data/data-table.js +339 -141
  156. package/dist/data/data-table.module.css +94 -0
  157. package/dist/data/data-table.module.css.d.ts +11 -0
  158. package/dist/data/virtual-list.d.ts.map +1 -1
  159. package/dist/data/virtual-list.js +10 -0
  160. package/dist/forms/form-layout.d.ts.map +1 -1
  161. package/dist/forms/form-layout.js +3 -3
  162. package/dist/forms/form-layout.module.css +30 -5
  163. package/dist/forms/form-layout.module.css.d.ts +1 -0
  164. package/dist/guard/index.d.ts +8 -2
  165. package/dist/guard/index.d.ts.map +1 -1
  166. package/dist/guard/index.js +67 -2
  167. package/dist/guard/types.d.ts +3 -3
  168. package/dist/guard/types.d.ts.map +1 -1
  169. package/dist/index.d.ts +1 -1
  170. package/dist/index.d.ts.map +1 -1
  171. package/dist/index.js +3 -1
  172. package/dist/provider/index.d.ts +20 -1
  173. package/dist/provider/index.d.ts.map +1 -1
  174. package/dist/provider/index.js +22 -2
  175. package/dist/shell/app-brand.d.ts.map +1 -1
  176. package/dist/shell/app-brand.js +1 -0
  177. package/dist/shell/app-brand.module.css +2 -2
  178. package/dist/shell/app-header.module.css +18 -8
  179. package/dist/shell/app-main.module.css +54 -3
  180. package/dist/shell/app-main.module.css.d.ts +1 -0
  181. package/dist/shell/app-mobile-nav.module.css +16 -4
  182. package/dist/shell/app-sidebar.module.css +4 -6
  183. package/dist/shell/index.d.ts +12 -0
  184. package/dist/shell/index.d.ts.map +1 -1
  185. package/dist/shell/index.js +57 -16
  186. package/dist/shell/mobile-nav-model.d.ts +1 -1
  187. package/dist/shell/page-aside.d.ts +22 -3
  188. package/dist/shell/page-aside.d.ts.map +1 -1
  189. package/dist/shell/page-aside.js +27 -13
  190. package/dist/shell/page-bar.d.ts.map +1 -1
  191. package/dist/shell/page-bar.js +10 -5
  192. package/dist/shell/page-bar.module.css +10 -34
  193. package/dist/shell/use-breakpoint.d.ts +3 -4
  194. package/dist/shell/use-breakpoint.d.ts.map +1 -1
  195. package/dist/shell/use-breakpoint.js +3 -25
  196. package/dist/shell/use-size-class.d.ts +6 -0
  197. package/dist/shell/use-size-class.d.ts.map +1 -0
  198. package/dist/shell/use-size-class.js +19 -0
  199. package/dist/state/persisted-or-local.d.ts +4 -1
  200. package/dist/state/persisted-or-local.d.ts.map +1 -1
  201. package/dist/state/persisted-or-local.js +4 -3
  202. package/dist/state/persisted.d.ts +10 -0
  203. package/dist/state/persisted.d.ts.map +1 -1
  204. package/dist/state/persisted.js +44 -14
  205. package/dist/styles.css +41 -0
  206. package/dist/surfaces.d.ts +4 -4
  207. package/dist/surfaces.d.ts.map +1 -1
  208. package/dist/surfaces.js +11 -3
  209. package/dist/theme/controls.module.css +7 -0
  210. package/dist/theme/ctl-theme.d.ts +1 -0
  211. package/dist/theme/ctl-theme.d.ts.map +1 -1
  212. package/dist/theme/ctl-theme.js +12 -4
  213. package/dist/theme/floating.module.css +36 -0
  214. package/dist/theme/floating.module.css.d.ts +3 -0
  215. package/dist/theme/index.d.ts +2 -2
  216. package/dist/theme/index.d.ts.map +1 -1
  217. package/dist/theme/index.js +34 -5
  218. package/dist/theme/nav-link.module.css +7 -0
  219. package/dist/tokens/index.d.ts +7 -16
  220. package/dist/tokens/index.d.ts.map +1 -1
  221. package/dist/tokens/index.js +23 -28
  222. package/dist/tokens/palette.d.ts +5 -27
  223. package/dist/tokens/palette.d.ts.map +1 -1
  224. package/dist/tokens/palette.js +8 -30
  225. package/dist/tokens/size-classes.d.ts +40 -0
  226. package/dist/tokens/size-classes.d.ts.map +1 -0
  227. package/dist/tokens/size-classes.js +29 -0
  228. package/dist/tokens.css +8 -4
  229. package/llms.txt +3 -3
  230. package/package.json +17 -17
  231. package/src/agent-chat/composer.tsx +74 -47
  232. package/src/agent-chat/thread-workspace.tsx +8 -11
  233. package/src/agent-chat/use-keyboard-inset.ts +68 -0
  234. package/src/charts/cursor/touch-pin.ts +138 -0
  235. package/src/charts/hooks/use-discrete-cursor.ts +335 -0
  236. package/src/charts/hooks/use-hysteresis.ts +26 -0
  237. package/src/charts/hooks/useBandPlot.ts +90 -27
  238. package/src/charts/hooks/useChartCursor.ts +211 -26
  239. package/src/charts/index.ts +0 -8
  240. package/src/charts/kinds/BandStrip.tsx +11 -1
  241. package/src/charts/kinds/Donut.tsx +153 -62
  242. package/src/charts/kinds/DualPanel.tsx +19 -4
  243. package/src/charts/kinds/Heatmap.tsx +61 -22
  244. package/src/charts/kinds/MirroredBars.tsx +15 -3
  245. package/src/charts/kinds/donut-layout.ts +82 -0
  246. package/src/charts/layout/auto-margin.ts +24 -5
  247. package/src/charts/layout/end-labels.ts +135 -0
  248. package/src/charts/primitives/Axes.tsx +112 -21
  249. package/src/charts/primitives/CartesianChart.tsx +282 -68
  250. package/src/charts/primitives/ChartCard.tsx +141 -48
  251. package/src/charts/primitives/ChartFrame.tsx +243 -60
  252. package/src/charts/primitives/ChartLegend.tsx +257 -73
  253. package/src/charts/primitives/ChartTooltip.tsx +8 -3
  254. package/src/charts/primitives/Crosshair.tsx +2 -2
  255. package/src/charts/primitives/HoverOverlay.module.css +13 -0
  256. package/src/charts/primitives/HoverOverlay.module.css.d.ts +8 -0
  257. package/src/charts/primitives/HoverOverlay.tsx +15 -4
  258. package/src/charts/primitives/chart-card-context.ts +37 -0
  259. package/src/charts/primitives/chart-frame-layout.ts +214 -177
  260. package/src/charts/primitives/chart-layout.ts +318 -0
  261. package/src/charts/primitives/chart-tier.tsx +66 -21
  262. package/src/charts/series.ts +4 -1
  263. package/src/charts/utils/ticks.ts +95 -19
  264. package/src/common/errors.ts +25 -1
  265. package/src/common/hit-floor.ts +9 -0
  266. package/src/{shell → common}/isomorphic-layout-effect.ts +4 -3
  267. package/src/common/use-measured-widths.ts +88 -0
  268. package/src/common/use-media-query.ts +56 -0
  269. package/src/common/validate.ts +17 -4
  270. package/src/content/article-layout.module.css +45 -4
  271. package/src/content/article-layout.module.css.d.ts +3 -0
  272. package/src/content/article-layout.tsx +60 -5
  273. package/src/content/code-block.module.css +32 -2
  274. package/src/content/code-block.tsx +47 -3
  275. package/src/content/prose.module.css +13 -8
  276. package/src/controls/actions.module.css +14 -1
  277. package/src/controls/actions.module.css.d.ts +1 -0
  278. package/src/controls/actions.tsx +223 -75
  279. package/src/controls/controls.module.css +8 -31
  280. package/src/controls/enum-filter.tsx +2 -2
  281. package/src/controls/filter-context.tsx +11 -0
  282. package/src/controls/filter-pill.tsx +1 -0
  283. package/src/controls/index.ts +0 -1
  284. package/src/controls/multi-select-filter.tsx +2 -2
  285. package/src/controls/sync-button.module.css +1 -1
  286. package/src/dashboard/section.module.css +1 -26
  287. package/src/dashboard/section.tsx +1 -0
  288. package/src/dashboard/stat-card.module.css +6 -9
  289. package/src/dashboard/stat-card.tsx +6 -3
  290. package/src/dashboard/stat-group.module.css +16 -4
  291. package/src/dashboard/widget-grid.tsx +7 -6
  292. package/src/dashboard/widget-header.module.css +145 -133
  293. package/src/dashboard/widget-header.module.css.d.ts +2 -0
  294. package/src/dashboard/widget-header.tsx +45 -11
  295. package/src/data/data-table.module.css +94 -0
  296. package/src/data/data-table.module.css.d.ts +11 -0
  297. package/src/data/data-table.tsx +503 -164
  298. package/src/data/virtual-list.tsx +31 -0
  299. package/src/forms/form-layout.module.css +30 -5
  300. package/src/forms/form-layout.module.css.d.ts +1 -0
  301. package/src/forms/form-layout.tsx +12 -10
  302. package/src/guard/index.ts +183 -3
  303. package/src/guard/types.ts +3 -2
  304. package/src/index.ts +2 -0
  305. package/src/provider/index.tsx +53 -1
  306. package/src/shell/app-brand.module.css +2 -2
  307. package/src/shell/app-brand.tsx +1 -0
  308. package/src/shell/app-header.module.css +18 -8
  309. package/src/shell/app-main.module.css +54 -3
  310. package/src/shell/app-main.module.css.d.ts +1 -0
  311. package/src/shell/app-mobile-nav.module.css +16 -4
  312. package/src/shell/app-sidebar.module.css +4 -6
  313. package/src/shell/index.tsx +200 -129
  314. package/src/shell/mobile-nav-model.ts +1 -1
  315. package/src/shell/page-aside.tsx +70 -21
  316. package/src/shell/page-bar.module.css +10 -34
  317. package/src/shell/page-bar.tsx +10 -5
  318. package/src/shell/use-breakpoint.ts +9 -45
  319. package/src/shell/use-size-class.ts +31 -0
  320. package/src/state/persisted-or-local.ts +10 -4
  321. package/src/state/persisted.ts +76 -17
  322. package/src/styles.css +41 -0
  323. package/src/surfaces.ts +9 -1
  324. package/src/theme/controls.module.css +7 -0
  325. package/src/theme/ctl-theme.tsx +15 -3
  326. package/src/theme/floating.module.css +36 -0
  327. package/src/theme/floating.module.css.d.ts +3 -0
  328. package/src/theme/index.ts +46 -8
  329. package/src/theme/nav-link.module.css +7 -0
  330. package/src/tokens/index.ts +20 -30
  331. package/src/tokens/palette.ts +6 -28
  332. package/src/tokens/size-classes.ts +46 -0
  333. package/dist/shell/isomorphic-layout-effect.d.ts.map +0 -1
  334. /package/dist/{shell → common}/isomorphic-layout-effect.js +0 -0
@@ -18,9 +18,11 @@ import {
18
18
  getSortedRowModel,
19
19
  useReactTable
20
20
  } from "@tanstack/react-table";
21
- import { useCallback, useLayoutEffect, useMemo, useRef, useState } from "react";
21
+ import { Fragment, useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
22
22
  import { cx } from "../common/props.js";
23
23
  import { BASALT_PREFIX } from "../common/errors.js";
24
+ import { HIT_COARSE } from "../common/hit-floor.js";
25
+ import { useMeasuredWidths } from "../common/use-measured-widths.js";
24
26
  import { assertRequiredProps, useValidateProps } from "../common/validate.js";
25
27
  import { FilterSet } from "../controls/index.js";
26
28
  import { ActionGroup, BarActionSlot } from "../controls/actions.js";
@@ -106,26 +108,31 @@ function selectionColumn() {
106
108
  return {
107
109
  id: SELECT_COLUMN_ID,
108
110
  enableSorting: false,
109
- header: ({ table }) => /* @__PURE__ */ jsx(
110
- Checkbox,
111
- {
112
- size: "xs",
113
- "aria-label": "Select all rows",
114
- checked: table.getIsAllPageRowsSelected(),
115
- indeterminate: table.getIsSomePageRowsSelected() && !table.getIsAllPageRowsSelected(),
116
- onChange: table.getToggleAllPageRowsSelectedHandler()
117
- }
111
+ header: ({ table }) => (
112
+ // oxlint-disable-next-line jsx-a11y/label-has-associated-control -- wraps <Checkbox>, which oxlint cannot see is an input
113
+ /* @__PURE__ */ jsx("label", { className: classes.selectHit, children: /* @__PURE__ */ jsx(
114
+ Checkbox,
115
+ {
116
+ size: "xs",
117
+ "aria-label": "Select all rows",
118
+ checked: table.getIsAllPageRowsSelected(),
119
+ indeterminate: table.getIsSomePageRowsSelected() && !table.getIsAllPageRowsSelected(),
120
+ onChange: table.getToggleAllPageRowsSelectedHandler()
121
+ }
122
+ ) })
118
123
  ),
119
- cell: ({ row }) => /* @__PURE__ */ jsx(
120
- Checkbox,
121
- {
122
- size: "xs",
123
- "aria-label": `Select row ${row.index + 1}`,
124
- checked: row.getIsSelected(),
125
- disabled: !row.getCanSelect(),
126
- onChange: row.getToggleSelectedHandler(),
127
- onClick: (event) => event.stopPropagation()
128
- }
124
+ cell: ({ row }) => (
125
+ // oxlint-disable-next-line jsx-a11y/label-has-associated-control -- wraps <Checkbox>, which oxlint cannot see is an input
126
+ /* @__PURE__ */ jsx("label", { className: classes.selectHit, children: /* @__PURE__ */ jsx(
127
+ Checkbox,
128
+ {
129
+ size: "xs",
130
+ "aria-label": `Select row ${row.index + 1}`,
131
+ checked: row.getIsSelected(),
132
+ disabled: !row.getCanSelect(),
133
+ onChange: row.getToggleSelectedHandler()
134
+ }
135
+ ) })
129
136
  )
130
137
  };
131
138
  }
@@ -201,6 +208,118 @@ function getPinnedCellStyle(table, column) {
201
208
  boxShadow: isEdge ? `-4px 0 6px -4px ${alpha(VX.ink, 0.18)}` : void 0
202
209
  };
203
210
  }
211
+ function columnDefId(column) {
212
+ if (column.id !== void 0) return column.id;
213
+ return "accessorKey" in column && typeof column.accessorKey === "string" ? column.accessorKey : void 0;
214
+ }
215
+ function foldOrder(columns) {
216
+ return columns.map((column, index) => ({
217
+ id: columnDefId(column),
218
+ priority: column.meta?.priority ?? index,
219
+ index
220
+ })).filter(
221
+ (candidate) => Boolean(candidate.id)
222
+ ).toSorted((a, b) => b.priority - a.priority || b.index - a.index).map((candidate) => candidate.id);
223
+ }
224
+ const FOLD_ID_ATTR = "data-basalt-fold-id";
225
+ const EMPTY_FOLD_SET = /* @__PURE__ */ new Set();
226
+ function planColumnFold(input) {
227
+ const { room, toggleWidth, overhead, order, widths } = input;
228
+ const known = order.filter((id) => widths.has(id));
229
+ let unfolded = overhead + known.reduce((sum, id) => sum + (widths.get(id) ?? 0), 0);
230
+ const folded = /* @__PURE__ */ new Set();
231
+ for (const id of known) {
232
+ if (folded.size >= known.length - 1) break;
233
+ const withToggle = unfolded + (folded.size > 0 ? toggleWidth : 0);
234
+ if (withToggle <= room) break;
235
+ folded.add(id);
236
+ unfolded -= widths.get(id) ?? 0;
237
+ }
238
+ return folded;
239
+ }
240
+ function useColumnFold(input) {
241
+ const { active, order, fixedIds } = input;
242
+ const wrapperRef = useRef(null);
243
+ const [folded, setFolded] = useState(EMPTY_FOLD_SET);
244
+ const orderRef = useRef(order);
245
+ orderRef.current = order;
246
+ const fixedIdsRef = useRef(fixedIds);
247
+ fixedIdsRef.current = fixedIds;
248
+ useLayoutEffect(() => {
249
+ if (!active || order.length === 0) {
250
+ setFolded((current) => current.size === 0 ? current : EMPTY_FOLD_SET);
251
+ }
252
+ }, [active, order.length]);
253
+ useMeasuredWidths({
254
+ resolveRoot: () => wrapperRef.current,
255
+ signature: `${order.join("|")}::${fixedIds.join("|")}`,
256
+ enabled: active && order.length > 0,
257
+ // The table too, not only the wrapper: a re-measured column set moves its min-content width
258
+ // while the wrapper's own box never changes.
259
+ extra: (root) => [root.querySelector("table")],
260
+ onMeasure: (root, boxes) => {
261
+ for (const cell of root.querySelectorAll(`[${FOLD_ID_ATTR}]`)) {
262
+ const id = cell.getAttribute(FOLD_ID_ATTR);
263
+ if (id !== null && cell.offsetWidth > 0) {
264
+ boxes.current.set(id, { width: cell.offsetWidth, height: cell.offsetHeight });
265
+ }
266
+ }
267
+ const room = root.clientWidth;
268
+ if (room === 0) return;
269
+ const widths = new Map([...boxes.current].map(([id, box]) => [id, box.width]));
270
+ const overhead = fixedIdsRef.current.reduce((sum, id) => sum + (widths.get(id) ?? 0), 0);
271
+ const next = planColumnFold({
272
+ room,
273
+ // The disclosure toggle is a fixed icon-button cell matched to the coarse hit floor, so it
274
+ // reads as one more `--vx-hit` control rather than a narrower afterthought.
275
+ toggleWidth: HIT_COARSE,
276
+ overhead,
277
+ order: orderRef.current,
278
+ widths
279
+ });
280
+ setFolded(
281
+ (current) => current.size === next.size && [...current].every((id) => next.has(id)) ? current : next
282
+ );
283
+ }
284
+ });
285
+ return { wrapperRef, folded: active ? folded : EMPTY_FOLD_SET };
286
+ }
287
+ function columnLabel(column) {
288
+ const header = column.columnDef.header;
289
+ return typeof header === "string" ? header : column.id;
290
+ }
291
+ function RowDisclosure({
292
+ row,
293
+ folded
294
+ }) {
295
+ const cells = row.getAllCells().filter((cell) => folded.has(cell.column.id));
296
+ return /* @__PURE__ */ jsx("div", { className: classes.foldDisclosure, children: cells.map((cell) => /* @__PURE__ */ jsxs("div", { className: classes.foldDisclosureRow, children: [
297
+ /* @__PURE__ */ jsx(Text, { className: classes.foldDisclosureLabel, children: columnLabel(cell.column) }),
298
+ /* @__PURE__ */ jsx("div", { className: classes.foldDisclosureValue, children: flexRender(cell.column.columnDef.cell, cell.getContext()) })
299
+ ] }, cell.id)) });
300
+ }
301
+ function FoldToggle({
302
+ expanded,
303
+ controlsId,
304
+ onToggle
305
+ }) {
306
+ return /* @__PURE__ */ jsx(
307
+ "button",
308
+ {
309
+ type: "button",
310
+ className: classes.foldToggle,
311
+ "data-basalt-hit": true,
312
+ "aria-expanded": expanded,
313
+ "aria-controls": controlsId,
314
+ "aria-label": expanded ? "Hide row details" : "Show row details",
315
+ onClick: (event) => {
316
+ event.stopPropagation();
317
+ onToggle();
318
+ },
319
+ children: /* @__PURE__ */ jsx(Box, { component: "span", "aria-hidden": true, children: expanded ? "\u25BE" : "\u25B8" })
320
+ }
321
+ );
322
+ }
204
323
  const BREACH_REMEDY = {
205
324
  inert: "`enablePagination` is not set, so `manualPagination` never reaches the table and the server page renders as if it were the whole set, with no bar to say otherwise. Pass `enablePagination`, or drop `manualPagination`.",
206
325
  total: 'neither `rowCount` nor `pageCount` was given, so "of N" counts only the rows on this page and the pager collapses to one page nobody can leave. Pass `rowCount={total}`.',
@@ -233,35 +352,21 @@ function enforceManualPaginationContract(breaches) {
233
352
  function useMeasuredContainment(active) {
234
353
  const wrapperRef = useRef(null);
235
354
  const [contained, setContained] = useState(false);
236
- useLayoutEffect(() => {
237
- if (!active) return;
238
- const wrapper = wrapperRef.current;
239
- if (wrapper === null) return;
240
- const checkFit = () => {
241
- const table2 = wrapper.querySelector("table");
242
- if (table2 === null) return "unknown";
243
- if (wrapper.clientWidth === 0 || table2.offsetWidth === 0) return "unknown";
244
- return table2.offsetWidth <= wrapper.clientWidth ? "fits" : "contained";
245
- };
246
- const apply = () => {
247
- const result = checkFit();
248
- if (result === "unknown") return;
249
- const next = result === "contained";
355
+ useMeasuredWidths({
356
+ resolveRoot: () => wrapperRef.current,
357
+ signature: String(active),
358
+ enabled: active,
359
+ // The TABLE too, not only the wrapper: a column set that re-measures (a facet filter changing
360
+ // the widest cell, a font settling) moves the table's min-content width while the wrapper's
361
+ // box never changes at all.
362
+ extra: (root) => [root.querySelector("table")],
363
+ onMeasure: (root) => {
364
+ const table = root.querySelector("table");
365
+ if (table === null || root.clientWidth === 0 || table.offsetWidth === 0) return;
366
+ const next = table.offsetWidth > root.clientWidth;
250
367
  setContained((current) => current === next ? current : next);
251
- };
252
- if (typeof ResizeObserver === "undefined") {
253
- apply();
254
- return;
255
368
  }
256
- const observer = new ResizeObserver(apply);
257
- observer.observe(wrapper);
258
- const table = wrapper.querySelector("table");
259
- if (table !== null) observer.observe(table);
260
- apply();
261
- return () => {
262
- observer.disconnect();
263
- };
264
- }, [active]);
369
+ });
265
370
  return { wrapperRef, contained: active && contained };
266
371
  }
267
372
  function BasaltDataTable(props) {
@@ -401,7 +506,7 @@ function BasaltDataTable(props) {
401
506
  if (!facets || facets.length === 0) return columns;
402
507
  const facetById = new Map(facets.map((facet) => [facet.columnId, facet]));
403
508
  return columns.map((column) => {
404
- const id = column.id ?? column.accessorKey;
509
+ const id = columnDefId(column);
405
510
  const facet = id === void 0 ? void 0 : facetById.get(id);
406
511
  if (!facet) return column;
407
512
  const filterFn = facet.multiple ? (row, columnId, filterValue) => !Array.isArray(filterValue) || filterValue.length === 0 ? true : filterValue.includes(String(row.getValue(columnId))) : (row, columnId, filterValue) => filterValue === null || filterValue === void 0 || filterValue === "" ? true : String(row.getValue(columnId)) === filterValue;
@@ -430,6 +535,62 @@ function BasaltDataTable(props) {
430
535
  const sortingEnabled = enableSorting && !breaches.includes("sorting");
431
536
  const filteringEnabled = !breaches.includes("filtering");
432
537
  const totalIsAuthoritative = !breaches.includes("total");
538
+ const scrolls = maxHeight !== void 0 || minWidth !== void 0;
539
+ const foldEligible = minWidth === void 0;
540
+ const { columnFoldOrder, columnFoldFixedIds } = useMemo(() => {
541
+ const pinnedIds = /* @__PURE__ */ new Set([...columnPinning.left ?? [], ...columnPinning.right ?? []]);
542
+ const excluded = new Set(
543
+ columns.flatMap((column) => {
544
+ const id = columnDefId(column);
545
+ if (id === void 0) return [];
546
+ return column.enableHiding === false || pinnedIds.has(id) ? [id] : [];
547
+ })
548
+ );
549
+ return {
550
+ columnFoldOrder: foldOrder(columns).filter((id) => !excluded.has(id)),
551
+ columnFoldFixedIds: [...excluded]
552
+ };
553
+ }, [columns, columnPinning]);
554
+ const columnFold = useColumnFold({
555
+ active: foldEligible,
556
+ order: columnFoldOrder,
557
+ fixedIds: columnFoldFixedIds
558
+ });
559
+ const columnVisibility = useMemo(
560
+ () => Object.fromEntries([...columnFold.folded].map((id) => [id, false])),
561
+ [columnFold.folded]
562
+ );
563
+ const hasFolded = columnFold.folded.size > 0;
564
+ const [expandedFoldRows, setExpandedFoldRows] = useState(EMPTY_FOLD_SET);
565
+ const toggleFoldRow = useCallback((rowId) => {
566
+ setExpandedFoldRows((current) => {
567
+ const next = new Set(current);
568
+ if (next.has(rowId)) next.delete(rowId);
569
+ else next.add(rowId);
570
+ return next;
571
+ });
572
+ }, []);
573
+ const prevDataRef = useRef(data);
574
+ useEffect(() => {
575
+ const prevData = prevDataRef.current;
576
+ prevDataRef.current = data;
577
+ setExpandedFoldRows((current) => {
578
+ if (current.size === 0) return current;
579
+ let next;
580
+ if (getRowId === void 0) {
581
+ next = new Set(
582
+ [...current].filter((id) => {
583
+ const index = Number(id);
584
+ return data[index] !== void 0 && data[index] === prevData[index];
585
+ })
586
+ );
587
+ } else {
588
+ const liveIds = new Set(data.map((row, index) => getRowId(row, index)));
589
+ next = new Set([...current].filter((id) => liveIds.has(id)));
590
+ }
591
+ return next.size === current.size ? current : next;
592
+ });
593
+ }, [data, getRowId]);
433
594
  const table = useReactTable({
434
595
  data,
435
596
  columns: tableColumns,
@@ -438,6 +599,7 @@ function BasaltDataTable(props) {
438
599
  globalFilter,
439
600
  columnFilters,
440
601
  columnPinning,
602
+ columnVisibility,
441
603
  ...enablePagination && { pagination },
442
604
  ...enableRowSelection && { rowSelection: rowSelectionState }
443
605
  },
@@ -476,7 +638,7 @@ function BasaltDataTable(props) {
476
638
  const branch = query === void 0 ? void 0 : dataQueryBranch("BasaltDataTable", query);
477
639
  const showSkeleton = branch === void 0 ? isLoading : branch === "pending";
478
640
  const queryError = branch === "error" && query !== void 0 ? query : void 0;
479
- const columnCount = columns.length + (enableRowSelection ? 1 : 0);
641
+ const columnCount = columns.length + (enableRowSelection ? 1 : 0) + (hasFolded ? 1 : 0) - columnFold.folded.size;
480
642
  const selectedModelRows = enableRowSelection ? table.getSelectedRowModel().rows : void 0;
481
643
  const selectedRows = useMemo(
482
644
  () => selectedModelRows?.map((row) => row.original) ?? [],
@@ -488,7 +650,6 @@ function BasaltDataTable(props) {
488
650
  );
489
651
  const headerGroups = enablePinning ? getOrderedHeaderGroups(table) : table.getHeaderGroups();
490
652
  const rows = table.getRowModel().rows;
491
- const scrolls = maxHeight !== void 0 || minWidth !== void 0;
492
653
  const pageStickyHeader = !scrolls && stickyHeader === true;
493
654
  const containment = useMeasuredContainment(pageStickyHeader);
494
655
  const resolvedStickyHeaderOffset = scrolls ? void 0 : stickyHeaderOffset;
@@ -507,36 +668,42 @@ function BasaltDataTable(props) {
507
668
  ...withRowBorders !== void 0 && { withRowBorders },
508
669
  withTableBorder,
509
670
  children: [
510
- /* @__PURE__ */ jsx(Table.Thead, { children: headerGroups.map((headerGroup) => /* @__PURE__ */ jsx(Table.Tr, { children: headerGroup.headers.map((header) => {
511
- const canSort = sortingEnabled && header.column.getCanSort();
512
- const sorted = header.column.getIsSorted();
513
- const toggleSorting = header.column.getToggleSortingHandler();
514
- const pinnedStyle = enablePinning ? getPinnedCellStyle(table, header.column) : void 0;
515
- const align = resolveAlign(header.column);
516
- return /* @__PURE__ */ jsxs(
517
- Table.Th,
518
- {
519
- onClick: canSort ? toggleSorting : void 0,
520
- onKeyDown: canSort ? (event) => {
521
- if (event.key !== "Enter" && event.key !== " ") return;
522
- event.preventDefault();
523
- toggleSorting?.(event);
524
- } : void 0,
525
- tabIndex: canSort ? 0 : void 0,
526
- "aria-sort": canSort ? sorted === "asc" ? "ascending" : sorted === "desc" ? "descending" : "none" : void 0,
527
- style: {
528
- ...canSort ? { cursor: "pointer", userSelect: "none" } : void 0,
529
- ...align !== void 0 && { textAlign: align },
530
- ...pinnedStyle
671
+ /* @__PURE__ */ jsx(Table.Thead, { children: headerGroups.map((headerGroup) => /* @__PURE__ */ jsxs(Table.Tr, { children: [
672
+ hasFolded && /* @__PURE__ */ jsx(Table.Th, { className: classes.foldToggleCell, "aria-hidden": true }),
673
+ headerGroup.headers.map((header) => {
674
+ const canSort = sortingEnabled && header.column.getCanSort();
675
+ const sorted = header.column.getIsSorted();
676
+ const toggleSorting = header.column.getToggleSortingHandler();
677
+ const pinnedStyle = enablePinning ? getPinnedCellStyle(table, header.column) : void 0;
678
+ const align = resolveAlign(header.column);
679
+ const isSelectColumn = header.column.id === SELECT_COLUMN_ID;
680
+ return /* @__PURE__ */ jsxs(
681
+ Table.Th,
682
+ {
683
+ ...isSelectColumn && { className: classes.selectCell },
684
+ ...!isSelectColumn && { [FOLD_ID_ATTR]: header.column.id },
685
+ onClick: canSort ? toggleSorting : void 0,
686
+ onKeyDown: canSort ? (event) => {
687
+ if (event.key !== "Enter" && event.key !== " ") return;
688
+ event.preventDefault();
689
+ toggleSorting?.(event);
690
+ } : void 0,
691
+ tabIndex: canSort ? 0 : void 0,
692
+ "aria-sort": canSort ? sorted === "asc" ? "ascending" : sorted === "desc" ? "descending" : "none" : void 0,
693
+ style: {
694
+ ...canSort ? { cursor: "pointer", userSelect: "none" } : void 0,
695
+ ...align !== void 0 && { textAlign: align },
696
+ ...pinnedStyle
697
+ },
698
+ children: [
699
+ header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext()),
700
+ canSort && /* @__PURE__ */ jsx(SortIndicator, { direction: sorted })
701
+ ]
531
702
  },
532
- children: [
533
- header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext()),
534
- canSort && /* @__PURE__ */ jsx(SortIndicator, { direction: sorted })
535
- ]
536
- },
537
- header.id
538
- );
539
- }) }, headerGroup.id)) }),
703
+ header.id
704
+ );
705
+ })
706
+ ] }, headerGroup.id)) }),
540
707
  /* @__PURE__ */ jsx(Table.Tbody, { children: queryError !== void 0 ? (
541
708
  // The branch the table did not have (components audit #3). It spans every rendered
542
709
  // column and carries the query's OWN `refetch` behind Retry, so the failure is reported
@@ -557,44 +724,65 @@ function BasaltDataTable(props) {
557
724
  ...row.getCenterVisibleCells(),
558
725
  ...row.getRightVisibleCells()
559
726
  ] : row.getVisibleCells();
560
- return /* @__PURE__ */ jsx(
561
- Table.Tr,
562
- {
563
- ...enableRowSelection && row.getIsSelected() && { "data-selected": true },
564
- ...onRowActivate !== void 0 && {
565
- className: classes.activatable,
566
- "data-activatable": true,
567
- tabIndex: 0,
568
- onClick: () => onRowActivate(row.original),
569
- // Enter only. Space is the browser's own page-scroll on a focused non-button, and
570
- // stealing it from a keyboard reader moving down a long table costs more than the
571
- // second activation key buys.
572
- onKeyDown: (event) => {
573
- if (event.key !== "Enter") return;
574
- if (event.target !== event.currentTarget) return;
575
- event.preventDefault();
576
- onRowActivate(row.original);
577
- }
578
- },
579
- children: cells.map((cell) => {
580
- const pinnedStyle = enablePinning ? getPinnedCellStyle(table, cell.column) : void 0;
581
- const align = resolveAlign(cell.column);
582
- return /* @__PURE__ */ jsx(
583
- Table.Td,
584
- {
585
- style: {
586
- ...typeof cell.getValue() === "number" && cell.column.columnDef.meta?.numeral !== false ? NUMERIC_CELL_STYLE : void 0,
587
- ...align !== void 0 && { textAlign: align },
588
- ...pinnedStyle
589
- },
590
- children: flexRender(cell.column.columnDef.cell, cell.getContext())
591
- },
592
- cell.id
593
- );
594
- })
595
- },
596
- row.id
597
- );
727
+ const rowExpanded = hasFolded && expandedFoldRows.has(row.id);
728
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
729
+ /* @__PURE__ */ jsxs(
730
+ Table.Tr,
731
+ {
732
+ ...enableRowSelection && row.getIsSelected() && { "data-selected": true },
733
+ ...onRowActivate !== void 0 && {
734
+ className: classes.activatable,
735
+ "data-activatable": true,
736
+ tabIndex: 0,
737
+ onClick: () => onRowActivate(row.original),
738
+ // Enter only. Space is the browser's own page-scroll on a focused non-button, and
739
+ // stealing it from a keyboard reader moving down a long table costs more than the
740
+ // second activation key buys.
741
+ onKeyDown: (event) => {
742
+ if (event.key !== "Enter") return;
743
+ if (event.target !== event.currentTarget) return;
744
+ event.preventDefault();
745
+ onRowActivate(row.original);
746
+ }
747
+ },
748
+ children: [
749
+ hasFolded && /* @__PURE__ */ jsx(Table.Td, { className: classes.foldToggleCell, children: /* @__PURE__ */ jsx(
750
+ FoldToggle,
751
+ {
752
+ expanded: rowExpanded,
753
+ controlsId: `${row.id}-fold`,
754
+ onToggle: () => toggleFoldRow(row.id)
755
+ }
756
+ ) }),
757
+ cells.map((cell) => {
758
+ const pinnedStyle = enablePinning ? getPinnedCellStyle(table, cell.column) : void 0;
759
+ const align = resolveAlign(cell.column);
760
+ return /* @__PURE__ */ jsx(
761
+ Table.Td,
762
+ {
763
+ ...cell.column.id === SELECT_COLUMN_ID && {
764
+ className: classes.selectCell,
765
+ // The label's ::after fills the cell, so a tap on it bubbles to the row —
766
+ // stop it only where a tick is possible; a disabled row's tap is a row tap.
767
+ ...cell.row.getCanSelect() && {
768
+ onClick: (event) => event.stopPropagation()
769
+ }
770
+ },
771
+ style: {
772
+ ...typeof cell.getValue() === "number" && cell.column.columnDef.meta?.numeral !== false ? NUMERIC_CELL_STYLE : void 0,
773
+ ...align !== void 0 && { textAlign: align },
774
+ ...pinnedStyle
775
+ },
776
+ children: flexRender(cell.column.columnDef.cell, cell.getContext())
777
+ },
778
+ cell.id
779
+ );
780
+ })
781
+ ]
782
+ }
783
+ ),
784
+ rowExpanded && /* @__PURE__ */ jsx(Table.Tr, { id: `${row.id}-fold`, className: classes.foldDisclosureRowTr, children: /* @__PURE__ */ jsx(Table.Td, { colSpan: columnCount, className: classes.foldDisclosureCell, children: /* @__PURE__ */ jsx(RowDisclosure, { row, folded: columnFold.folded }) }) })
785
+ ] }, row.id);
598
786
  }) })
599
787
  ]
600
788
  }
@@ -661,27 +849,36 @@ function BasaltDataTable(props) {
661
849
  ) })
662
850
  ] }),
663
851
  bulkBarActions !== void 0 && /* @__PURE__ */ jsx(BulkActionBar, { count: selectedRows.length, actions: bulkBarActions }),
664
- pageStickyHeader ? (
665
- // Pinning takes this wrapper too — a pinned column's offsets are `position: sticky` on the
666
- // cells and need no overflow box of their own, so the `overflow-x: auto` Box that used to
667
- // sit here was the exact inert-sticky defect this shape exists to avoid.
668
- /* @__PURE__ */ jsx(
669
- "div",
670
- {
671
- ref: containment.wrapperRef,
672
- className: classes.containment,
673
- "data-contained": containment.contained ? "true" : "false",
674
- children: tableNode
675
- }
676
- )
677
- ) : /* @__PURE__ */ jsx(
678
- Table.ScrollContainer,
679
- {
680
- type: "native",
681
- minWidth: minWidth ?? 0,
682
- ...maxHeight !== void 0 && { maxHeight },
683
- children: tableNode
684
- }
852
+ !foldEligible ? (
853
+ // An explicit `minWidth` is the one genuinely DECLARED horizontal floor — the column fold
854
+ // never engages here (see `foldEligible`, above), so this shape keeps its exact
855
+ // pre-existing DOM: no extra measuring wrapper for a table nothing folds.
856
+ /* @__PURE__ */ jsx(Table.ScrollContainer, { type: "native", minWidth: minWidth ?? 0, maxHeight, children: tableNode })
857
+ ) : (
858
+ // The column fold's own measuring box (`useColumnFold`) — wraps every remaining shape,
859
+ // `maxHeight`-only included: a vertical cap is orthogonal to column width, so a
860
+ // `stickyHeader` + `maxHeight` table (the pairing this package's own docs recommend) still
861
+ // folds instead of silently regressing to horizontal scroll. A plain block box, so it
862
+ // constrains nothing on its own; `columnFold.wrapperRef` only reads it.
863
+ /* @__PURE__ */ jsx("div", { ref: columnFold.wrapperRef, className: classes.foldWrapper, children: pageStickyHeader ? (
864
+ // Pinning takes this wrapper too — a pinned column's offsets are `position: sticky` on
865
+ // the cells and need no overflow box of their own, so the `overflow-x: auto` Box that
866
+ // used to sit here was the exact inert-sticky defect this shape exists to avoid.
867
+ /* @__PURE__ */ jsx(
868
+ "div",
869
+ {
870
+ ref: containment.wrapperRef,
871
+ className: classes.containment,
872
+ "data-contained": containment.contained ? "true" : "false",
873
+ children: tableNode
874
+ }
875
+ )
876
+ ) : maxHeight !== void 0 ? (
877
+ // `pageStickyHeader` is already false whenever `maxHeight` is set (`scrolls` above), so
878
+ // this is the ONE remaining case: a vertical cap with no sticky header to measure —
879
+ // still wants its own `ScrollArea` scrollport, just with no `minWidth` floor.
880
+ /* @__PURE__ */ jsx(Table.ScrollContainer, { type: "native", minWidth: 0, maxHeight, children: tableNode })
881
+ ) : /* @__PURE__ */ jsx(Table.ScrollContainer, { type: "native", minWidth: 0, children: tableNode }) })
685
882
  ),
686
883
  enablePagination && /* @__PURE__ */ jsxs(
687
884
  Group,
@@ -713,7 +910,7 @@ function BasaltDataTable(props) {
713
910
  allowDeselect: false
714
911
  }
715
912
  ),
716
- /* @__PURE__ */ jsx(
913
+ /* @__PURE__ */ jsx(CtlSlot, { children: /* @__PURE__ */ jsx(
717
914
  Pagination,
718
915
  {
719
916
  size: "sm",
@@ -722,7 +919,7 @@ function BasaltDataTable(props) {
722
919
  value: paginationState.pageIndex + 1,
723
920
  onChange: (page) => table.setPageIndex(page - 1)
724
921
  }
725
- )
922
+ ) })
726
923
  ] })
727
924
  ]
728
925
  }
@@ -730,5 +927,6 @@ function BasaltDataTable(props) {
730
927
  ] });
731
928
  }
732
929
  export {
733
- BasaltDataTable
930
+ BasaltDataTable,
931
+ planColumnFold
734
932
  };