basalt-ui 1.27.0 → 1.28.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 (1063) hide show
  1. package/AGENTS.md +23 -23
  2. package/CHANGELOG.md +59 -0
  3. package/MIGRATING.md +613 -20
  4. package/README.md +33 -3
  5. package/agent/rules/basalt-batteries.md +25 -10
  6. package/agent/rules/basalt-controls.md +56 -31
  7. package/agent/rules/basalt-mantine.md +17 -6
  8. package/agent/rules/basalt-tokens.md +1 -1
  9. package/configs/oxlint-plugin.js +1056 -28
  10. package/configs/oxlint-plugin.test.ts +1066 -0
  11. package/configs/oxlint.json +7 -0
  12. package/dist/agent/coalesce.d.ts.map +1 -1
  13. package/dist/agent/coalesce.js +2 -1
  14. package/dist/agent/merge.d.ts.map +1 -1
  15. package/dist/agent/merge.js +2 -1
  16. package/dist/agent/part-list.d.ts.map +1 -1
  17. package/dist/agent/part-list.js +2 -0
  18. package/dist/agent/stick-to-bottom.d.ts +3 -6
  19. package/dist/agent/stick-to-bottom.d.ts.map +1 -1
  20. package/dist/agent/stick-to-bottom.js +6 -3
  21. package/dist/agent-chat/composer.d.ts +6 -2
  22. package/dist/agent-chat/composer.d.ts.map +1 -1
  23. package/dist/agent-chat/composer.js +49 -33
  24. package/dist/agent-chat/relative-time.d.ts +0 -9
  25. package/dist/agent-chat/relative-time.d.ts.map +1 -1
  26. package/dist/agent-chat/relative-time.js +2 -1
  27. package/dist/agent-chat/thread-detail-panel.d.ts +3 -2
  28. package/dist/agent-chat/thread-detail-panel.d.ts.map +1 -1
  29. package/dist/agent-chat/thread-detail-panel.js +8 -5
  30. package/dist/agent-chat/thread-feed-row.d.ts +2 -1
  31. package/dist/agent-chat/thread-feed-row.d.ts.map +1 -1
  32. package/dist/agent-chat/thread-feed-row.js +92 -92
  33. package/dist/agent-chat/thread-feed.d.ts +3 -2
  34. package/dist/agent-chat/thread-feed.d.ts.map +1 -1
  35. package/dist/agent-chat/thread-feed.js +8 -3
  36. package/dist/agent-chat/thread-message.d.ts +4 -3
  37. package/dist/agent-chat/thread-message.d.ts.map +1 -1
  38. package/dist/agent-chat/thread-message.js +70 -31
  39. package/dist/agent-chat/thread-outcome-card.d.ts +3 -2
  40. package/dist/agent-chat/thread-outcome-card.d.ts.map +1 -1
  41. package/dist/agent-chat/thread-outcome-card.js +9 -2
  42. package/dist/agent-chat/thread-workspace.d.ts +7 -2
  43. package/dist/agent-chat/thread-workspace.d.ts.map +1 -1
  44. package/dist/agent-chat/thread-workspace.js +62 -29
  45. package/dist/agent-chat/tool-chip.d.ts +3 -2
  46. package/dist/agent-chat/tool-chip.d.ts.map +1 -1
  47. package/dist/agent-chat/tool-chip.js +7 -2
  48. package/dist/charts/hooks/useBandPlot.d.ts.map +1 -1
  49. package/dist/charts/hooks/useBandPlot.js +15 -8
  50. package/dist/charts/index.d.ts +8 -7
  51. package/dist/charts/index.d.ts.map +1 -1
  52. package/dist/charts/index.js +45 -5
  53. package/dist/charts/kinds/BandStrip.d.ts +10 -7
  54. package/dist/charts/kinds/BandStrip.d.ts.map +1 -1
  55. package/dist/charts/kinds/BandStrip.js +12 -7
  56. package/dist/charts/kinds/Bars.d.ts +12 -6
  57. package/dist/charts/kinds/Bars.d.ts.map +1 -1
  58. package/dist/charts/kinds/Bars.js +37 -11
  59. package/dist/charts/kinds/Donut.d.ts +6 -5
  60. package/dist/charts/kinds/Donut.d.ts.map +1 -1
  61. package/dist/charts/kinds/Donut.js +19 -2
  62. package/dist/charts/kinds/DualPanel.d.ts +6 -5
  63. package/dist/charts/kinds/DualPanel.d.ts.map +1 -1
  64. package/dist/charts/kinds/DualPanel.js +36 -8
  65. package/dist/charts/kinds/Heatmap.d.ts +10 -6
  66. package/dist/charts/kinds/Heatmap.d.ts.map +1 -1
  67. package/dist/charts/kinds/Heatmap.js +58 -31
  68. package/dist/charts/kinds/MirroredBars.d.ts +6 -5
  69. package/dist/charts/kinds/MirroredBars.d.ts.map +1 -1
  70. package/dist/charts/kinds/MirroredBars.js +9 -2
  71. package/dist/charts/kinds/MultiLine.d.ts +11 -6
  72. package/dist/charts/kinds/MultiLine.d.ts.map +1 -1
  73. package/dist/charts/kinds/MultiLine.js +24 -15
  74. package/dist/charts/kinds/StackedArea.d.ts +18 -8
  75. package/dist/charts/kinds/StackedArea.d.ts.map +1 -1
  76. package/dist/charts/kinds/StackedArea.js +29 -4
  77. package/dist/charts/kinds/ZonedLine.d.ts +12 -8
  78. package/dist/charts/kinds/ZonedLine.d.ts.map +1 -1
  79. package/dist/charts/kinds/ZonedLine.js +33 -23
  80. package/dist/charts/layout/auto-margin.d.ts +65 -8
  81. package/dist/charts/layout/auto-margin.d.ts.map +1 -1
  82. package/dist/charts/layout/auto-margin.js +45 -10
  83. package/dist/charts/layout/log-ticks.d.ts +27 -0
  84. package/dist/charts/layout/log-ticks.d.ts.map +1 -0
  85. package/dist/charts/layout/log-ticks.js +42 -0
  86. package/dist/charts/primitives/Axes.d.ts +11 -2
  87. package/dist/charts/primitives/Axes.d.ts.map +1 -1
  88. package/dist/charts/primitives/Axes.js +15 -5
  89. package/dist/charts/primitives/CartesianChart.d.ts +45 -7
  90. package/dist/charts/primitives/CartesianChart.d.ts.map +1 -1
  91. package/dist/charts/primitives/CartesianChart.js +86 -28
  92. package/dist/charts/primitives/ChartCard.d.ts +27 -2
  93. package/dist/charts/primitives/ChartCard.d.ts.map +1 -1
  94. package/dist/charts/primitives/ChartCard.js +37 -17
  95. package/dist/charts/primitives/ChartFrame.d.ts +30 -4
  96. package/dist/charts/primitives/ChartFrame.d.ts.map +1 -1
  97. package/dist/charts/primitives/ChartFrame.js +45 -15
  98. package/dist/charts/primitives/ChartLegend.d.ts +13 -2
  99. package/dist/charts/primitives/ChartLegend.d.ts.map +1 -1
  100. package/dist/charts/primitives/ChartLegend.js +33 -13
  101. package/dist/charts/primitives/ChartPending.d.ts +77 -4
  102. package/dist/charts/primitives/ChartPending.d.ts.map +1 -1
  103. package/dist/charts/primitives/ChartPending.js +100 -6
  104. package/dist/charts/primitives/ChartTooltip.d.ts +7 -0
  105. package/dist/charts/primitives/ChartTooltip.d.ts.map +1 -1
  106. package/dist/charts/primitives/ChartTooltip.js +18 -5
  107. package/dist/charts/primitives/Crosshair.d.ts +3 -0
  108. package/dist/charts/primitives/Crosshair.d.ts.map +1 -1
  109. package/dist/charts/primitives/Crosshair.js +3 -1
  110. package/dist/charts/primitives/chart-frame-layout.d.ts +95 -0
  111. package/dist/charts/primitives/chart-frame-layout.d.ts.map +1 -0
  112. package/dist/charts/primitives/chart-frame-layout.js +89 -0
  113. package/dist/charts/primitives/chart-tier.d.ts +16 -0
  114. package/dist/charts/primitives/chart-tier.d.ts.map +1 -0
  115. package/dist/charts/primitives/chart-tier.js +20 -0
  116. package/dist/charts/series.d.ts +55 -1
  117. package/dist/charts/series.d.ts.map +1 -1
  118. package/dist/charts/series.js +27 -1
  119. package/dist/charts/sparklines/BarSparkline.d.ts +3 -3
  120. package/dist/charts/sparklines/BarSparkline.d.ts.map +1 -1
  121. package/dist/charts/sparklines/BarSparkline.js +9 -3
  122. package/dist/charts/sparklines/LineSparkline.d.ts +3 -3
  123. package/dist/charts/sparklines/LineSparkline.d.ts.map +1 -1
  124. package/dist/charts/sparklines/LineSparkline.js +15 -3
  125. package/dist/charts/sparklines/index.d.ts +2 -2
  126. package/dist/charts/sparklines/index.d.ts.map +1 -1
  127. package/dist/charts/utils/format.d.ts +92 -2
  128. package/dist/charts/utils/format.d.ts.map +1 -1
  129. package/dist/charts/utils/format.js +64 -1
  130. package/dist/charts/utils/ticks.d.ts +81 -2
  131. package/dist/charts/utils/ticks.d.ts.map +1 -1
  132. package/dist/charts/utils/ticks.js +54 -4
  133. package/dist/cli/index.d.ts.map +1 -1
  134. package/dist/cli/index.js +2 -2
  135. package/dist/commands/ShortcutsHelp.d.ts +3 -2
  136. package/dist/commands/ShortcutsHelp.d.ts.map +1 -1
  137. package/dist/commands/ShortcutsHelp.js +8 -2
  138. package/dist/commands/define-commands.d.ts.map +1 -1
  139. package/dist/commands/define-commands.js +2 -2
  140. package/dist/commands/define-overlays.d.ts +102 -0
  141. package/dist/commands/define-overlays.d.ts.map +1 -1
  142. package/dist/commands/define-overlays.js +102 -2
  143. package/dist/commands/index.d.ts +10 -1
  144. package/dist/commands/index.d.ts.map +1 -1
  145. package/dist/commands/index.js +3 -1
  146. package/dist/commands/overlays-mount.d.ts +13 -6
  147. package/dist/commands/overlays-mount.d.ts.map +1 -1
  148. package/dist/commands/overlays-mount.js +23 -14
  149. package/dist/common/errors.d.ts +34 -0
  150. package/dist/common/errors.d.ts.map +1 -0
  151. package/dist/common/errors.js +36 -0
  152. package/dist/common/index.d.ts +15 -0
  153. package/dist/common/index.d.ts.map +1 -0
  154. package/dist/common/index.js +29 -0
  155. package/dist/common/props.d.ts +63 -0
  156. package/dist/common/props.d.ts.map +1 -0
  157. package/dist/common/props.js +6 -0
  158. package/dist/common/query-state-like.d.ts +43 -0
  159. package/dist/common/query-state-like.d.ts.map +1 -0
  160. package/dist/common/query-state-like.js +20 -0
  161. package/dist/common/refs.d.ts +29 -0
  162. package/dist/common/refs.d.ts.map +1 -0
  163. package/dist/common/refs.js +29 -0
  164. package/dist/common/scroll-parent.d.ts +23 -0
  165. package/dist/common/scroll-parent.d.ts.map +1 -0
  166. package/dist/common/scroll-parent.js +21 -0
  167. package/dist/common/validate.d.ts +38 -0
  168. package/dist/common/validate.d.ts.map +1 -0
  169. package/dist/common/validate.js +36 -0
  170. package/dist/connectivity/connectivity-indicator.d.ts +3 -1
  171. package/dist/connectivity/connectivity-indicator.d.ts.map +1 -1
  172. package/dist/connectivity/connectivity-indicator.js +3 -1
  173. package/dist/connectivity/connectivity-provider.d.ts.map +1 -1
  174. package/dist/connectivity/connectivity-provider.js +4 -1
  175. package/dist/connectivity/index.d.ts +1 -0
  176. package/dist/connectivity/index.d.ts.map +1 -1
  177. package/dist/connectivity/types.d.ts +2 -2
  178. package/dist/connectivity/types.d.ts.map +1 -1
  179. package/dist/connectivity/use-connectivity.d.ts.map +1 -1
  180. package/dist/content/article-layout.module.css +11 -4
  181. package/dist/content/callout.d.ts +8 -4
  182. package/dist/content/callout.d.ts.map +1 -1
  183. package/dist/content/callout.js +15 -8
  184. package/dist/content/guide.d.ts +5 -4
  185. package/dist/content/guide.d.ts.map +1 -1
  186. package/dist/content/guide.js +13 -4
  187. package/dist/content/heading-anchor.d.ts +24 -2
  188. package/dist/content/heading-anchor.d.ts.map +1 -1
  189. package/dist/content/heading-anchor.js +23 -9
  190. package/dist/content/index.d.ts.map +1 -1
  191. package/dist/content/markdown.d.ts.map +1 -1
  192. package/dist/content/markdown.js +2 -0
  193. package/dist/content/prose.module.css +9 -7
  194. package/dist/content/reading-progress.d.ts +10 -4
  195. package/dist/content/reading-progress.d.ts.map +1 -1
  196. package/dist/content/reading-progress.js +26 -10
  197. package/dist/content/slug.d.ts +4 -0
  198. package/dist/content/slug.d.ts.map +1 -1
  199. package/dist/content/toc.d.ts +11 -0
  200. package/dist/content/toc.d.ts.map +1 -1
  201. package/dist/content/toc.js +23 -10
  202. package/dist/controls/actions.d.ts +49 -6
  203. package/dist/controls/actions.d.ts.map +1 -1
  204. package/dist/controls/actions.js +39 -8
  205. package/dist/controls/compare-filter.d.ts +3 -2
  206. package/dist/controls/compare-filter.d.ts.map +1 -1
  207. package/dist/controls/compare-filter.js +17 -2
  208. package/dist/controls/control-group.d.ts +3 -3
  209. package/dist/controls/control-group.d.ts.map +1 -1
  210. package/dist/controls/control-group.js +9 -2
  211. package/dist/controls/controls.module.css +169 -186
  212. package/dist/controls/controls.module.css.d.ts +9 -11
  213. package/dist/controls/enum-filter.d.ts +3 -2
  214. package/dist/controls/enum-filter.d.ts.map +1 -1
  215. package/dist/controls/enum-filter.js +28 -13
  216. package/dist/controls/filter-context.d.ts +8 -4
  217. package/dist/controls/filter-context.d.ts.map +1 -1
  218. package/dist/controls/filter-pill.d.ts +3 -3
  219. package/dist/controls/filter-pill.d.ts.map +1 -1
  220. package/dist/controls/filter-pill.js +4 -1
  221. package/dist/controls/filter-set.d.ts +3 -2
  222. package/dist/controls/filter-set.d.ts.map +1 -1
  223. package/dist/controls/filter-set.js +66 -41
  224. package/dist/controls/filter-sheet.d.ts +20 -80
  225. package/dist/controls/filter-sheet.d.ts.map +1 -1
  226. package/dist/controls/filter-sheet.js +9 -95
  227. package/dist/controls/index.d.ts +2 -0
  228. package/dist/controls/index.d.ts.map +1 -1
  229. package/dist/controls/index.js +4 -0
  230. package/dist/controls/multi-select-filter.d.ts +18 -2
  231. package/dist/controls/multi-select-filter.d.ts.map +1 -1
  232. package/dist/controls/multi-select-filter.js +122 -20
  233. package/dist/controls/number-filter.d.ts +7 -5
  234. package/dist/controls/number-filter.d.ts.map +1 -1
  235. package/dist/controls/number-filter.js +40 -11
  236. package/dist/controls/panel-row.d.ts +132 -0
  237. package/dist/controls/panel-row.d.ts.map +1 -0
  238. package/dist/controls/panel-row.js +173 -0
  239. package/dist/controls/panel-row.module.css +86 -0
  240. package/dist/controls/panel-row.module.css.d.ts +13 -0
  241. package/dist/controls/range-filter.d.ts +2 -1
  242. package/dist/controls/range-filter.d.ts.map +1 -1
  243. package/dist/controls/range-filter.js +57 -35
  244. package/dist/controls/search-filter.d.ts +3 -2
  245. package/dist/controls/search-filter.d.ts.map +1 -1
  246. package/dist/controls/search-filter.js +25 -10
  247. package/dist/controls/select-filter.d.ts +2 -1
  248. package/dist/controls/select-filter.d.ts.map +1 -1
  249. package/dist/controls/select-filter.js +4 -0
  250. package/dist/controls/slider-control.d.ts +36 -0
  251. package/dist/controls/slider-control.d.ts.map +1 -0
  252. package/dist/controls/slider-control.js +64 -0
  253. package/dist/controls/sync-button.d.ts +3 -2
  254. package/dist/controls/sync-button.d.ts.map +1 -1
  255. package/dist/controls/sync-button.js +16 -3
  256. package/dist/controls/toggle-filter.d.ts +3 -2
  257. package/dist/controls/toggle-filter.d.ts.map +1 -1
  258. package/dist/controls/toggle-filter.js +28 -11
  259. package/dist/controls/view-tabs.d.ts +3 -2
  260. package/dist/controls/view-tabs.d.ts.map +1 -1
  261. package/dist/controls/view-tabs.js +43 -11
  262. package/dist/controls-dates/date-range-picker.d.ts +1 -1
  263. package/dist/controls-dates/date-range-picker.d.ts.map +1 -1
  264. package/dist/controls-dates/date-range-picker.js +6 -1
  265. package/dist/dashboard/delta-badge.d.ts +5 -1
  266. package/dist/dashboard/delta-badge.d.ts.map +1 -1
  267. package/dist/dashboard/empty-state.d.ts +10 -3
  268. package/dist/dashboard/empty-state.d.ts.map +1 -1
  269. package/dist/dashboard/empty-state.js +37 -6
  270. package/dist/dashboard/index.d.ts +6 -4
  271. package/dist/dashboard/index.d.ts.map +1 -1
  272. package/dist/dashboard/index.js +7 -1
  273. package/dist/dashboard/query-state.d.ts +48 -27
  274. package/dist/dashboard/query-state.d.ts.map +1 -1
  275. package/dist/dashboard/query-state.js +111 -57
  276. package/dist/dashboard/settings-section.d.ts +19 -9
  277. package/dist/dashboard/settings-section.d.ts.map +1 -1
  278. package/dist/dashboard/settings-section.js +74 -25
  279. package/dist/dashboard/stat-card.d.ts +44 -38
  280. package/dist/dashboard/stat-card.d.ts.map +1 -1
  281. package/dist/dashboard/stat-card.js +55 -42
  282. package/dist/dashboard/stat-card.module.css +46 -31
  283. package/dist/dashboard/stat-card.module.css.d.ts +1 -0
  284. package/dist/dashboard/stat-group.d.ts +41 -0
  285. package/dist/dashboard/stat-group.d.ts.map +1 -0
  286. package/dist/dashboard/stat-group.js +30 -0
  287. package/dist/dashboard/stat-group.module.css +66 -0
  288. package/dist/dashboard/stat-group.module.css.d.ts +8 -0
  289. package/dist/dashboard/widget-grid.d.ts +27 -0
  290. package/dist/dashboard/widget-grid.d.ts.map +1 -0
  291. package/dist/dashboard/widget-grid.js +41 -0
  292. package/dist/dashboard/widget-grid.module.css +53 -0
  293. package/dist/dashboard/widget-grid.module.css.d.ts +9 -0
  294. package/dist/data/data-table.d.ts +151 -12
  295. package/dist/data/data-table.d.ts.map +1 -1
  296. package/dist/data/data-table.js +384 -138
  297. package/dist/data/data-table.module.css +64 -0
  298. package/dist/data/data-table.module.css.d.ts +11 -0
  299. package/dist/data/index.d.ts +2 -2
  300. package/dist/data/index.d.ts.map +1 -1
  301. package/dist/data/index.js +24 -2
  302. package/dist/data/query-branch.d.ts +28 -0
  303. package/dist/data/query-branch.d.ts.map +1 -0
  304. package/dist/data/query-branch.js +10 -0
  305. package/dist/data/table.d.ts +2 -2
  306. package/dist/data/table.d.ts.map +1 -1
  307. package/dist/data/virtual-list.d.ts +65 -6
  308. package/dist/data/virtual-list.d.ts.map +1 -1
  309. package/dist/data/virtual-list.js +89 -38
  310. package/dist/data/virtual.d.ts +1 -1
  311. package/dist/data/virtual.d.ts.map +1 -1
  312. package/dist/forms/FormErrorSummary.d.ts +4 -3
  313. package/dist/forms/FormErrorSummary.d.ts.map +1 -1
  314. package/dist/forms/FormErrorSummary.js +6 -4
  315. package/dist/forms/create-form.d.ts +2 -2
  316. package/dist/forms/field.d.ts +46 -6
  317. package/dist/forms/field.d.ts.map +1 -1
  318. package/dist/forms/field.js +10 -5
  319. package/dist/forms/form-layout.d.ts +142 -0
  320. package/dist/forms/form-layout.d.ts.map +1 -0
  321. package/dist/forms/form-layout.js +110 -0
  322. package/dist/forms/form-layout.module.css +145 -0
  323. package/dist/forms/form-layout.module.css.d.ts +20 -0
  324. package/dist/forms/form-state.d.ts +42 -0
  325. package/dist/forms/form-state.d.ts.map +1 -0
  326. package/dist/forms/form-state.js +26 -0
  327. package/dist/forms/index.d.ts +22 -6
  328. package/dist/forms/index.d.ts.map +1 -1
  329. package/dist/forms/index.js +19 -3
  330. package/dist/forms/use-field-array.d.ts +60 -0
  331. package/dist/forms/use-field-array.d.ts.map +1 -0
  332. package/dist/forms/use-field-array.js +20 -0
  333. package/dist/forms/use-form-draft.d.ts +25 -10
  334. package/dist/forms/use-form-draft.d.ts.map +1 -1
  335. package/dist/forms/use-form-draft.js +55 -18
  336. package/dist/forms/use-form-submit.d.ts +88 -0
  337. package/dist/forms/use-form-submit.d.ts.map +1 -0
  338. package/dist/forms/use-form-submit.js +63 -0
  339. package/dist/guard/index.d.ts.map +1 -1
  340. package/dist/guard/index.js +26 -6
  341. package/dist/index.d.ts +10 -8
  342. package/dist/index.d.ts.map +1 -1
  343. package/dist/index.js +44 -4
  344. package/dist/nav/types.d.ts +15 -6
  345. package/dist/nav/types.d.ts.map +1 -1
  346. package/dist/notifications/bell.d.ts +3 -2
  347. package/dist/notifications/bell.d.ts.map +1 -1
  348. package/dist/notifications/bell.js +73 -56
  349. package/dist/notifications/center.d.ts +15 -4
  350. package/dist/notifications/center.d.ts.map +1 -1
  351. package/dist/notifications/center.js +5 -2
  352. package/dist/notifications/define-notifications.d.ts +56 -14
  353. package/dist/notifications/define-notifications.d.ts.map +1 -1
  354. package/dist/notifications/define-notifications.js +2 -1
  355. package/dist/notifications/index.d.ts +6 -2
  356. package/dist/notifications/index.d.ts.map +1 -1
  357. package/dist/notifications/index.js +5 -1
  358. package/dist/notifications/mount-guard.d.ts +8 -0
  359. package/dist/notifications/mount-guard.d.ts.map +1 -0
  360. package/dist/notifications/mount-guard.js +19 -0
  361. package/dist/notifications/notify.d.ts +58 -1
  362. package/dist/notifications/notify.d.ts.map +1 -1
  363. package/dist/notifications/notify.js +46 -2
  364. package/dist/notifications/overlay.d.ts.map +1 -1
  365. package/dist/notifications/overlay.js +2 -0
  366. package/dist/notifications/store.d.ts +7 -0
  367. package/dist/notifications/store.d.ts.map +1 -1
  368. package/dist/provider/index.d.ts +37 -5
  369. package/dist/provider/index.d.ts.map +1 -1
  370. package/dist/provider/index.js +66 -28
  371. package/dist/provider/lab-theme.d.ts.map +1 -1
  372. package/dist/provider/lab-theme.js +2 -1
  373. package/dist/query/devtools.d.ts.map +1 -1
  374. package/dist/query/devtools.js +1 -0
  375. package/dist/router-tanstack/field.d.ts +1 -1
  376. package/dist/router-tanstack/field.d.ts.map +1 -1
  377. package/dist/router-tanstack/search-store.d.ts.map +1 -1
  378. package/dist/router-tanstack/search-store.js +1 -1
  379. package/dist/router-tanstack/use-nav.d.ts.map +1 -1
  380. package/dist/router-tanstack/use-nav.js +1 -1
  381. package/dist/section/index.d.ts +1 -1
  382. package/dist/section/index.d.ts.map +1 -1
  383. package/dist/section/section.d.ts +36 -2
  384. package/dist/section/section.d.ts.map +1 -1
  385. package/dist/section/section.js +49 -57
  386. package/dist/section/section.module.css +11 -0
  387. package/dist/shell/account-types.d.ts +5 -2
  388. package/dist/shell/account-types.d.ts.map +1 -1
  389. package/dist/shell/app-brand.d.ts +13 -0
  390. package/dist/shell/app-brand.d.ts.map +1 -0
  391. package/dist/shell/app-brand.js +94 -0
  392. package/dist/shell/app-brand.module.css +109 -0
  393. package/dist/shell/app-brand.module.css.d.ts +14 -0
  394. package/dist/shell/app-breadcrumbs.d.ts +4 -2
  395. package/dist/shell/app-breadcrumbs.d.ts.map +1 -1
  396. package/dist/shell/app-breadcrumbs.js +33 -22
  397. package/dist/shell/app-header.module.css +10 -5
  398. package/dist/shell/app-main.module.css +107 -0
  399. package/dist/shell/app-main.module.css.d.ts +11 -0
  400. package/dist/shell/app-mobile-nav.d.ts +3 -2
  401. package/dist/shell/app-mobile-nav.d.ts.map +1 -1
  402. package/dist/shell/app-mobile-nav.js +29 -15
  403. package/dist/shell/app-mobile-nav.module.css +48 -27
  404. package/dist/shell/app-mobile-nav.module.css.d.ts +0 -1
  405. package/dist/shell/app-sidebar-account.d.ts +2 -1
  406. package/dist/shell/app-sidebar-account.d.ts.map +1 -1
  407. package/dist/shell/app-sidebar-account.js +57 -22
  408. package/dist/shell/app-sidebar.d.ts +9 -3
  409. package/dist/shell/app-sidebar.d.ts.map +1 -1
  410. package/dist/shell/app-sidebar.js +55 -109
  411. package/dist/shell/app-sidebar.module.css +53 -96
  412. package/dist/shell/app-sidebar.module.css.d.ts +0 -5
  413. package/dist/shell/index.d.ts +14 -6
  414. package/dist/shell/index.d.ts.map +1 -1
  415. package/dist/shell/index.js +47 -12
  416. package/dist/shell/isomorphic-layout-effect.d.ts +14 -0
  417. package/dist/shell/isomorphic-layout-effect.d.ts.map +1 -0
  418. package/dist/shell/isomorphic-layout-effect.js +5 -0
  419. package/dist/shell/mobile-nav-model.d.ts.map +1 -1
  420. package/dist/shell/mobile-nav-model.js +27 -6
  421. package/dist/shell/nav-count-badge.d.ts +5 -3
  422. package/dist/shell/nav-count-badge.d.ts.map +1 -1
  423. package/dist/shell/nav-count-badge.js +3 -1
  424. package/dist/shell/page-aside.d.ts +43 -0
  425. package/dist/shell/page-aside.d.ts.map +1 -0
  426. package/dist/shell/page-aside.js +202 -0
  427. package/dist/shell/page-aside.module.css +174 -0
  428. package/dist/shell/page-aside.module.css.d.ts +14 -0
  429. package/dist/shell/page-bar.d.ts +55 -15
  430. package/dist/shell/page-bar.d.ts.map +1 -1
  431. package/dist/shell/page-bar.js +130 -41
  432. package/dist/shell/page-bar.module.css +107 -13
  433. package/dist/shell/page-bar.module.css.d.ts +4 -1
  434. package/dist/shell/sidebar-search.d.ts +4 -2
  435. package/dist/shell/sidebar-search.d.ts.map +1 -1
  436. package/dist/shell/sidebar-search.js +9 -4
  437. package/dist/shell/sidebar-search.module.css +1 -1
  438. package/dist/state/fields.d.ts +23 -3
  439. package/dist/state/fields.d.ts.map +1 -1
  440. package/dist/state/fields.js +13 -2
  441. package/dist/state/persisted-or-local.d.ts +9 -0
  442. package/dist/state/persisted-or-local.d.ts.map +1 -0
  443. package/dist/state/persisted-or-local.js +24 -0
  444. package/dist/state.d.ts +1 -1
  445. package/dist/state.d.ts.map +1 -1
  446. package/dist/state.js +2 -6
  447. package/dist/surfaces.d.ts +12 -11
  448. package/dist/surfaces.d.ts.map +1 -1
  449. package/dist/surfaces.js +42 -12
  450. package/dist/theme/ctl-theme.d.ts.map +1 -1
  451. package/dist/theme/index.d.ts.map +1 -1
  452. package/dist/theme/index.js +102 -17
  453. package/dist/theme/nav-link.module.css +37 -26
  454. package/dist/theme/segmented-control.module.css +22 -0
  455. package/dist/theme/segmented-control.module.css.d.ts +1 -0
  456. package/dist/theme-lab/boot.d.ts +66 -0
  457. package/dist/theme-lab/boot.d.ts.map +1 -0
  458. package/dist/theme-lab/boot.js +82 -0
  459. package/dist/theme-lab/index.d.ts +4 -43
  460. package/dist/theme-lab/index.d.ts.map +1 -1
  461. package/dist/theme-lab/index.js +9 -69
  462. package/dist/theme-toggle/index.d.ts +3 -2
  463. package/dist/theme-toggle/index.d.ts.map +1 -1
  464. package/dist/theme-toggle/index.js +8 -2
  465. package/dist/tokens/index.d.ts +42 -0
  466. package/dist/tokens/index.d.ts.map +1 -1
  467. package/dist/tokens/index.js +35 -6
  468. package/dist/tokens/palette.d.ts +45 -25
  469. package/dist/tokens/palette.d.ts.map +1 -1
  470. package/dist/tokens/palette.js +31 -14
  471. package/dist/tokens.css +4 -5
  472. package/dist/utils/is-dev.d.ts +3 -0
  473. package/dist/utils/is-dev.d.ts.map +1 -0
  474. package/dist/utils/is-dev.js +6 -0
  475. package/dist/widget-header/delta-badge.d.ts +7 -1
  476. package/dist/widget-header/delta-badge.d.ts.map +1 -1
  477. package/dist/widget-header/delta-badge.js +8 -1
  478. package/dist/widget-header/index.d.ts +2 -2
  479. package/dist/widget-header/index.d.ts.map +1 -1
  480. package/dist/widget-header/index.js +3 -1
  481. package/dist/widget-header/widget-header.d.ts +69 -4
  482. package/dist/widget-header/widget-header.d.ts.map +1 -1
  483. package/dist/widget-header/widget-header.js +42 -27
  484. package/dist/widget-header/widget-header.module.css +72 -7
  485. package/llms.txt +6 -6
  486. package/package.json +49 -25
  487. package/src/agent/coalesce.ts +2 -1
  488. package/src/agent/merge.ts +2 -1
  489. package/src/agent/part-list.tsx +2 -0
  490. package/src/agent/stick-to-bottom.tsx +9 -11
  491. package/src/agent-chat/composer.tsx +124 -103
  492. package/src/agent-chat/relative-time.ts +2 -1
  493. package/src/agent-chat/thread-detail-panel.tsx +9 -5
  494. package/src/agent-chat/thread-feed-row.tsx +17 -9
  495. package/src/agent-chat/thread-feed.tsx +9 -3
  496. package/src/agent-chat/thread-message.tsx +48 -13
  497. package/src/agent-chat/thread-outcome-card.tsx +9 -1
  498. package/src/agent-chat/thread-workspace.tsx +72 -33
  499. package/src/agent-chat/tool-chip.tsx +8 -2
  500. package/src/charts/hooks/useBandPlot.ts +26 -8
  501. package/src/charts/index.ts +39 -4
  502. package/src/charts/kinds/BandStrip.tsx +36 -14
  503. package/src/charts/kinds/Bars.tsx +67 -16
  504. package/src/charts/kinds/Donut.tsx +30 -3
  505. package/src/charts/kinds/DualPanel.tsx +56 -10
  506. package/src/charts/kinds/Heatmap.tsx +90 -33
  507. package/src/charts/kinds/MirroredBars.tsx +21 -3
  508. package/src/charts/kinds/MultiLine.tsx +61 -18
  509. package/src/charts/kinds/StackedArea.tsx +77 -6
  510. package/src/charts/kinds/ZonedLine.tsx +71 -31
  511. package/src/charts/layout/auto-margin.ts +134 -21
  512. package/src/charts/layout/log-ticks.ts +90 -0
  513. package/src/charts/primitives/Axes.tsx +33 -5
  514. package/src/charts/primitives/CartesianChart.tsx +194 -41
  515. package/src/charts/primitives/ChartCard.tsx +66 -25
  516. package/src/charts/primitives/ChartFrame.tsx +107 -26
  517. package/src/charts/primitives/ChartLegend.tsx +43 -13
  518. package/src/charts/primitives/ChartPending.tsx +185 -7
  519. package/src/charts/primitives/ChartTooltip.tsx +37 -5
  520. package/src/charts/primitives/Crosshair.tsx +6 -3
  521. package/src/charts/primitives/chart-frame-layout.ts +212 -0
  522. package/src/charts/primitives/chart-tier.tsx +37 -0
  523. package/src/charts/series.ts +77 -1
  524. package/src/charts/sparklines/BarSparkline.tsx +11 -3
  525. package/src/charts/sparklines/LineSparkline.tsx +18 -4
  526. package/src/charts/sparklines/index.ts +2 -2
  527. package/src/charts/utils/format.ts +163 -2
  528. package/src/charts/utils/ticks.ts +149 -4
  529. package/src/cli/index.ts +8 -3
  530. package/src/commands/ShortcutsHelp.tsx +10 -3
  531. package/src/commands/define-commands.ts +2 -2
  532. package/src/commands/define-overlays.ts +216 -1
  533. package/src/commands/index.ts +12 -0
  534. package/src/commands/overlays-mount.tsx +60 -26
  535. package/src/common/errors.ts +73 -0
  536. package/src/common/index.ts +21 -0
  537. package/src/common/props.ts +71 -0
  538. package/src/common/query-state-like.ts +64 -0
  539. package/src/common/refs.ts +58 -0
  540. package/src/common/scroll-parent.ts +42 -0
  541. package/src/common/validate.ts +94 -0
  542. package/src/connectivity/connectivity-indicator.tsx +6 -1
  543. package/src/connectivity/connectivity-provider.tsx +4 -1
  544. package/src/connectivity/index.ts +1 -0
  545. package/src/connectivity/types.ts +5 -2
  546. package/src/connectivity/use-connectivity.ts +3 -2
  547. package/src/content/article-layout.module.css +11 -4
  548. package/src/content/callout.tsx +13 -7
  549. package/src/content/guide.tsx +14 -4
  550. package/src/content/heading-anchor.tsx +14 -3
  551. package/src/content/index.ts +2 -0
  552. package/src/content/markdown.tsx +2 -0
  553. package/src/content/prose.module.css +9 -7
  554. package/src/content/reading-progress.tsx +40 -15
  555. package/src/content/slug.ts +4 -0
  556. package/src/content/toc.tsx +47 -10
  557. package/src/controls/actions.tsx +89 -9
  558. package/src/controls/compare-filter.tsx +20 -3
  559. package/src/controls/control-group.tsx +11 -4
  560. package/src/controls/controls.module.css +169 -186
  561. package/src/controls/controls.module.css.d.ts +9 -11
  562. package/src/controls/enum-filter.tsx +32 -16
  563. package/src/controls/filter-context.tsx +14 -6
  564. package/src/controls/filter-pill.tsx +6 -3
  565. package/src/controls/filter-set.tsx +32 -13
  566. package/src/controls/filter-sheet.tsx +44 -194
  567. package/src/controls/index.ts +7 -0
  568. package/src/controls/multi-select-filter.tsx +177 -27
  569. package/src/controls/number-filter.tsx +61 -23
  570. package/src/controls/panel-row.module.css +86 -0
  571. package/src/controls/panel-row.module.css.d.ts +13 -0
  572. package/src/controls/panel-row.tsx +392 -0
  573. package/src/controls/range-filter.tsx +76 -39
  574. package/src/controls/search-filter.tsx +30 -12
  575. package/src/controls/select-filter.tsx +11 -1
  576. package/src/controls/slider-control.tsx +159 -0
  577. package/src/controls/sync-button.tsx +13 -3
  578. package/src/controls/toggle-filter.tsx +36 -16
  579. package/src/controls/view-tabs.tsx +63 -11
  580. package/src/controls-dates/date-range-picker.tsx +8 -1
  581. package/src/dashboard/delta-badge.tsx +5 -1
  582. package/src/dashboard/empty-state.tsx +72 -25
  583. package/src/dashboard/index.ts +13 -1
  584. package/src/dashboard/query-state.tsx +154 -84
  585. package/src/dashboard/settings-section.tsx +87 -31
  586. package/src/dashboard/stat-card.module.css +46 -31
  587. package/src/dashboard/stat-card.module.css.d.ts +1 -0
  588. package/src/dashboard/stat-card.tsx +185 -127
  589. package/src/dashboard/stat-group.module.css +66 -0
  590. package/src/dashboard/stat-group.module.css.d.ts +8 -0
  591. package/src/dashboard/stat-group.tsx +74 -0
  592. package/src/dashboard/widget-grid.module.css +53 -0
  593. package/src/dashboard/widget-grid.module.css.d.ts +9 -0
  594. package/src/dashboard/widget-grid.tsx +127 -0
  595. package/src/data/data-table.module.css +64 -0
  596. package/src/data/data-table.module.css.d.ts +11 -0
  597. package/src/data/data-table.tsx +919 -346
  598. package/src/data/index.ts +30 -2
  599. package/src/data/query-branch.ts +53 -0
  600. package/src/data/table.ts +5 -0
  601. package/src/data/virtual-list.tsx +189 -55
  602. package/src/data/virtual.ts +5 -1
  603. package/src/forms/FormErrorSummary.tsx +13 -6
  604. package/src/forms/create-form.ts +2 -2
  605. package/src/forms/field.ts +60 -11
  606. package/src/forms/form-layout.module.css +145 -0
  607. package/src/forms/form-layout.module.css.d.ts +20 -0
  608. package/src/forms/form-layout.tsx +305 -0
  609. package/src/forms/form-state.tsx +94 -0
  610. package/src/forms/index.ts +38 -7
  611. package/src/forms/use-field-array.ts +94 -0
  612. package/src/forms/use-form-draft.ts +145 -53
  613. package/src/forms/use-form-submit.ts +199 -0
  614. package/src/guard/index.ts +73 -18
  615. package/src/index.ts +74 -10
  616. package/src/nav/types.ts +15 -6
  617. package/src/notifications/bell.tsx +18 -4
  618. package/src/notifications/center.tsx +18 -5
  619. package/src/notifications/define-notifications.ts +64 -18
  620. package/src/notifications/index.ts +12 -1
  621. package/src/notifications/mount-guard.ts +41 -0
  622. package/src/notifications/notify.tsx +122 -3
  623. package/src/notifications/overlay.tsx +2 -0
  624. package/src/notifications/store.ts +7 -0
  625. package/src/provider/index.tsx +101 -13
  626. package/src/provider/lab-theme.ts +2 -1
  627. package/src/query/devtools.tsx +1 -0
  628. package/src/router-tanstack/field.ts +1 -0
  629. package/src/router-tanstack/search-store.ts +3 -10
  630. package/src/router-tanstack/use-nav.ts +18 -13
  631. package/src/section/index.ts +1 -1
  632. package/src/section/section.module.css +11 -0
  633. package/src/section/section.tsx +163 -96
  634. package/src/shell/account-types.ts +5 -2
  635. package/src/shell/app-brand.module.css +109 -0
  636. package/src/shell/app-brand.module.css.d.ts +14 -0
  637. package/src/shell/app-brand.tsx +140 -0
  638. package/src/shell/app-breadcrumbs.tsx +19 -9
  639. package/src/shell/app-header.module.css +10 -5
  640. package/src/shell/app-main.module.css +107 -0
  641. package/src/shell/app-main.module.css.d.ts +11 -0
  642. package/src/shell/app-mobile-nav.module.css +48 -27
  643. package/src/shell/app-mobile-nav.module.css.d.ts +0 -1
  644. package/src/shell/app-mobile-nav.tsx +43 -11
  645. package/src/shell/app-sidebar-account.tsx +29 -6
  646. package/src/shell/app-sidebar.module.css +53 -96
  647. package/src/shell/app-sidebar.module.css.d.ts +0 -5
  648. package/src/shell/app-sidebar.tsx +40 -113
  649. package/src/shell/index.tsx +156 -65
  650. package/src/shell/isomorphic-layout-effect.ts +14 -0
  651. package/src/shell/mobile-nav-model.ts +66 -10
  652. package/src/shell/nav-count-badge.tsx +6 -1
  653. package/src/shell/page-aside.module.css +174 -0
  654. package/src/shell/page-aside.module.css.d.ts +14 -0
  655. package/src/shell/page-aside.tsx +377 -0
  656. package/src/shell/page-bar.module.css +107 -13
  657. package/src/shell/page-bar.module.css.d.ts +4 -1
  658. package/src/shell/page-bar.tsx +294 -94
  659. package/src/shell/sidebar-search.module.css +1 -1
  660. package/src/shell/sidebar-search.tsx +13 -4
  661. package/src/state/fields.ts +43 -10
  662. package/src/state/persisted-or-local.ts +50 -0
  663. package/src/state.ts +0 -9
  664. package/src/surfaces.ts +42 -12
  665. package/src/theme/ctl-theme.tsx +10 -0
  666. package/src/theme/index.ts +129 -16
  667. package/src/theme/nav-link.module.css +37 -26
  668. package/src/theme/segmented-control.module.css +22 -0
  669. package/src/theme/segmented-control.module.css.d.ts +1 -0
  670. package/src/theme-lab/boot.ts +140 -0
  671. package/src/theme-lab/index.tsx +21 -112
  672. package/src/theme-toggle/index.tsx +10 -3
  673. package/src/tokens/index.ts +50 -6
  674. package/src/tokens/palette.ts +58 -30
  675. package/src/utils/is-dev.ts +4 -0
  676. package/src/widget-header/delta-badge.tsx +25 -5
  677. package/src/widget-header/index.ts +10 -2
  678. package/src/widget-header/widget-header.module.css +72 -7
  679. package/src/widget-header/widget-header.tsx +150 -71
  680. package/dist/agent/adapter.test.d.ts +0 -2
  681. package/dist/agent/adapter.test.d.ts.map +0 -1
  682. package/dist/agent/ai-sdk-transport.test.d.ts +0 -2
  683. package/dist/agent/ai-sdk-transport.test.d.ts.map +0 -1
  684. package/dist/agent/coalesce.test.d.ts +0 -2
  685. package/dist/agent/coalesce.test.d.ts.map +0 -1
  686. package/dist/agent/foreign.test.d.ts +0 -2
  687. package/dist/agent/foreign.test.d.ts.map +0 -1
  688. package/dist/agent/id.test.d.ts +0 -2
  689. package/dist/agent/id.test.d.ts.map +0 -1
  690. package/dist/agent/merge.test.d.ts +0 -2
  691. package/dist/agent/merge.test.d.ts.map +0 -1
  692. package/dist/agent/part-list.test.d.ts +0 -2
  693. package/dist/agent/part-list.test.d.ts.map +0 -1
  694. package/dist/agent/parts.test.d.ts +0 -2
  695. package/dist/agent/parts.test.d.ts.map +0 -1
  696. package/dist/agent/thread.test.d.ts +0 -2
  697. package/dist/agent/thread.test.d.ts.map +0 -1
  698. package/dist/agent/transport.test.d.ts +0 -2
  699. package/dist/agent/transport.test.d.ts.map +0 -1
  700. package/dist/agent/use-agent-stream.test.d.ts +0 -2
  701. package/dist/agent/use-agent-stream.test.d.ts.map +0 -1
  702. package/dist/agent/use-agent-thread-runs.foreign.test.d.ts +0 -2
  703. package/dist/agent/use-agent-thread-runs.foreign.test.d.ts.map +0 -1
  704. package/dist/agent/use-agent-thread-runs.resume.test.d.ts +0 -2
  705. package/dist/agent/use-agent-thread-runs.resume.test.d.ts.map +0 -1
  706. package/dist/agent/use-agent-thread-runs.test.d.ts +0 -2
  707. package/dist/agent/use-agent-thread-runs.test.d.ts.map +0 -1
  708. package/dist/agent/use-agent-thread-runs.wedge.test.d.ts +0 -2
  709. package/dist/agent/use-agent-thread-runs.wedge.test.d.ts.map +0 -1
  710. package/dist/agent-chat/composer.test.d.ts +0 -2
  711. package/dist/agent-chat/composer.test.d.ts.map +0 -1
  712. package/dist/agent-chat/relative-time.test.d.ts +0 -2
  713. package/dist/agent-chat/relative-time.test.d.ts.map +0 -1
  714. package/dist/agent-chat/thread-feed-row.test.d.ts +0 -2
  715. package/dist/agent-chat/thread-feed-row.test.d.ts.map +0 -1
  716. package/dist/agent-chat/thread-feed.test.d.ts +0 -2
  717. package/dist/agent-chat/thread-feed.test.d.ts.map +0 -1
  718. package/dist/agent-chat/thread-message.test.d.ts +0 -2
  719. package/dist/agent-chat/thread-message.test.d.ts.map +0 -1
  720. package/dist/agent-chat/thread-workspace.test.d.ts +0 -2
  721. package/dist/agent-chat/thread-workspace.test.d.ts.map +0 -1
  722. package/dist/agent-chat/tool-chip.test.d.ts +0 -2
  723. package/dist/agent-chat/tool-chip.test.d.ts.map +0 -1
  724. package/dist/agent-chat/virtualize.type-guard.test.d.ts +0 -2
  725. package/dist/agent-chat/virtualize.type-guard.test.d.ts.map +0 -1
  726. package/dist/charts/cursor/resolve.test.d.ts +0 -2
  727. package/dist/charts/cursor/resolve.test.d.ts.map +0 -1
  728. package/dist/charts/cursor/store.test.d.ts +0 -2
  729. package/dist/charts/cursor/store.test.d.ts.map +0 -1
  730. package/dist/charts/kinds/BandStrip.test.d.ts +0 -2
  731. package/dist/charts/kinds/BandStrip.test.d.ts.map +0 -1
  732. package/dist/charts/kinds/Bars.test.d.ts +0 -2
  733. package/dist/charts/kinds/Bars.test.d.ts.map +0 -1
  734. package/dist/charts/kinds/DualPanel.test.d.ts +0 -2
  735. package/dist/charts/kinds/DualPanel.test.d.ts.map +0 -1
  736. package/dist/charts/kinds/MirroredBars.test.d.ts +0 -2
  737. package/dist/charts/kinds/MirroredBars.test.d.ts.map +0 -1
  738. package/dist/charts/kinds/MultiLine.test.d.ts +0 -2
  739. package/dist/charts/kinds/MultiLine.test.d.ts.map +0 -1
  740. package/dist/charts/kinds/StackedArea.test.d.ts +0 -2
  741. package/dist/charts/kinds/StackedArea.test.d.ts.map +0 -1
  742. package/dist/charts/kinds/ZonedLine.test.d.ts +0 -2
  743. package/dist/charts/kinds/ZonedLine.test.d.ts.map +0 -1
  744. package/dist/charts/layout/auto-margin.test.d.ts +0 -2
  745. package/dist/charts/layout/auto-margin.test.d.ts.map +0 -1
  746. package/dist/charts/primitives/CartesianChart.test.d.ts +0 -2
  747. package/dist/charts/primitives/CartesianChart.test.d.ts.map +0 -1
  748. package/dist/charts/primitives/ChartCard.test.d.ts +0 -2
  749. package/dist/charts/primitives/ChartCard.test.d.ts.map +0 -1
  750. package/dist/charts/primitives/ChartFrame.test.d.ts +0 -2
  751. package/dist/charts/primitives/ChartFrame.test.d.ts.map +0 -1
  752. package/dist/charts/primitives/ChartLegend.test.d.ts +0 -2
  753. package/dist/charts/primitives/ChartLegend.test.d.ts.map +0 -1
  754. package/dist/charts/primitives/ChartTooltip.test.d.ts +0 -2
  755. package/dist/charts/primitives/ChartTooltip.test.d.ts.map +0 -1
  756. package/dist/charts/primitives/XZoneRects.test.d.ts +0 -2
  757. package/dist/charts/primitives/XZoneRects.test.d.ts.map +0 -1
  758. package/dist/charts/series.test.d.ts +0 -2
  759. package/dist/charts/series.test.d.ts.map +0 -1
  760. package/dist/charts/utils/domain.test.d.ts +0 -2
  761. package/dist/charts/utils/domain.test.d.ts.map +0 -1
  762. package/dist/charts/utils/measure-text.test.d.ts +0 -2
  763. package/dist/charts/utils/measure-text.test.d.ts.map +0 -1
  764. package/dist/cli/check-coverage.test.d.ts +0 -2
  765. package/dist/cli/check-coverage.test.d.ts.map +0 -1
  766. package/dist/cli/check-theme.test.d.ts +0 -2
  767. package/dist/cli/check-theme.test.d.ts.map +0 -1
  768. package/dist/cli/help.test.d.ts +0 -2
  769. package/dist/cli/help.test.d.ts.map +0 -1
  770. package/dist/cli/init-sync.test.d.ts +0 -2
  771. package/dist/cli/init-sync.test.d.ts.map +0 -1
  772. package/dist/cli/placement-engine.test.d.ts +0 -2
  773. package/dist/cli/placement-engine.test.d.ts.map +0 -1
  774. package/dist/cli/shipped-versions.test.d.ts +0 -2
  775. package/dist/cli/shipped-versions.test.d.ts.map +0 -1
  776. package/dist/cli/tarball-parity.test.d.ts +0 -2
  777. package/dist/cli/tarball-parity.test.d.ts.map +0 -1
  778. package/dist/cli/template-format.test.d.ts +0 -2
  779. package/dist/cli/template-format.test.d.ts.map +0 -1
  780. package/dist/cli/template-ledger.test.d.ts +0 -2
  781. package/dist/cli/template-ledger.test.d.ts.map +0 -1
  782. package/dist/cli/tokens-css.test.d.ts +0 -2
  783. package/dist/cli/tokens-css.test.d.ts.map +0 -1
  784. package/dist/cli/toolchain-wiring.test.d.ts +0 -2
  785. package/dist/cli/toolchain-wiring.test.d.ts.map +0 -1
  786. package/dist/cli/waiver-audit.test.d.ts +0 -2
  787. package/dist/cli/waiver-audit.test.d.ts.map +0 -1
  788. package/dist/content/article-actions.test.d.ts +0 -2
  789. package/dist/content/article-actions.test.d.ts.map +0 -1
  790. package/dist/content/article-model.test.d.ts +0 -2
  791. package/dist/content/article-model.test.d.ts.map +0 -1
  792. package/dist/content/block-split.test.d.ts +0 -2
  793. package/dist/content/block-split.test.d.ts.map +0 -1
  794. package/dist/content/heading-components.test.d.ts +0 -2
  795. package/dist/content/heading-components.test.d.ts.map +0 -1
  796. package/dist/content/markdown.test.d.ts +0 -2
  797. package/dist/content/markdown.test.d.ts.map +0 -1
  798. package/dist/content/prose.module.css.test.d.ts +0 -2
  799. package/dist/content/prose.module.css.test.d.ts.map +0 -1
  800. package/dist/content/sanitize.test.d.ts +0 -2
  801. package/dist/content/sanitize.test.d.ts.map +0 -1
  802. package/dist/content/slug.test.d.ts +0 -2
  803. package/dist/content/slug.test.d.ts.map +0 -1
  804. package/dist/controls/actions.test.d.ts +0 -2
  805. package/dist/controls/actions.test.d.ts.map +0 -1
  806. package/dist/controls/actions.type-guard.test.d.ts +0 -2
  807. package/dist/controls/actions.type-guard.test.d.ts.map +0 -1
  808. package/dist/controls/control-group.test.d.ts +0 -2
  809. package/dist/controls/control-group.test.d.ts.map +0 -1
  810. package/dist/controls/controls.router.test.d.ts +0 -2
  811. package/dist/controls/controls.router.test.d.ts.map +0 -1
  812. package/dist/controls/filter-set.test.d.ts +0 -2
  813. package/dist/controls/filter-set.test.d.ts.map +0 -1
  814. package/dist/controls/peer-boundary.test.d.ts +0 -2
  815. package/dist/controls/peer-boundary.test.d.ts.map +0 -1
  816. package/dist/controls/props-contract.test.d.ts +0 -2
  817. package/dist/controls/props-contract.test.d.ts.map +0 -1
  818. package/dist/controls/range-filter.type-guard.test.d.ts +0 -2
  819. package/dist/controls/range-filter.type-guard.test.d.ts.map +0 -1
  820. package/dist/controls/sync-button.test.d.ts +0 -2
  821. package/dist/controls/sync-button.test.d.ts.map +0 -1
  822. package/dist/dashboard/query-state.test.d.ts +0 -2
  823. package/dist/dashboard/query-state.test.d.ts.map +0 -1
  824. package/dist/dashboard/settings-section.test.d.ts +0 -2
  825. package/dist/dashboard/settings-section.test.d.ts.map +0 -1
  826. package/dist/dashboard/stat-card.test.d.ts +0 -2
  827. package/dist/dashboard/stat-card.test.d.ts.map +0 -1
  828. package/dist/data/data-table.test.d.ts +0 -2
  829. package/dist/data/data-table.test.d.ts.map +0 -1
  830. package/dist/guard/check-source.test.d.ts +0 -2
  831. package/dist/guard/check-source.test.d.ts.map +0 -1
  832. package/dist/guard/grace.test.d.ts +0 -20
  833. package/dist/guard/grace.test.d.ts.map +0 -1
  834. package/dist/guard/guard-hook.test.d.ts +0 -2
  835. package/dist/guard/guard-hook.test.d.ts.map +0 -1
  836. package/dist/provider/index.test.d.ts +0 -2
  837. package/dist/provider/index.test.d.ts.map +0 -1
  838. package/dist/provider/lab-theme.test.d.ts +0 -2
  839. package/dist/provider/lab-theme.test.d.ts.map +0 -1
  840. package/dist/query/error-message.test.d.ts +0 -2
  841. package/dist/query/error-message.test.d.ts.map +0 -1
  842. package/dist/router-tanstack/multi-search-param-store.test.d.ts +0 -2
  843. package/dist/router-tanstack/multi-search-param-store.test.d.ts.map +0 -1
  844. package/dist/router-tanstack/nav.test.d.ts +0 -2
  845. package/dist/router-tanstack/nav.test.d.ts.map +0 -1
  846. package/dist/router-tanstack/search-param-store.test.d.ts +0 -2
  847. package/dist/router-tanstack/search-param-store.test.d.ts.map +0 -1
  848. package/dist/router-tanstack/search-store.router.test.d.ts +0 -2
  849. package/dist/router-tanstack/search-store.router.test.d.ts.map +0 -1
  850. package/dist/router-tanstack/search-store.test.d.ts +0 -2
  851. package/dist/router-tanstack/search-store.test.d.ts.map +0 -1
  852. package/dist/router-tanstack/use-nav.test.d.ts +0 -2
  853. package/dist/router-tanstack/use-nav.test.d.ts.map +0 -1
  854. package/dist/section/section.test.d.ts +0 -2
  855. package/dist/section/section.test.d.ts.map +0 -1
  856. package/dist/shell/app-mobile-nav.test.d.ts +0 -2
  857. package/dist/shell/app-mobile-nav.test.d.ts.map +0 -1
  858. package/dist/shell/app-sidebar.test.d.ts +0 -2
  859. package/dist/shell/app-sidebar.test.d.ts.map +0 -1
  860. package/dist/shell/index.test.d.ts +0 -2
  861. package/dist/shell/index.test.d.ts.map +0 -1
  862. package/dist/shell/mobile-nav-model.test.d.ts +0 -2
  863. package/dist/shell/mobile-nav-model.test.d.ts.map +0 -1
  864. package/dist/shell/page-bar.test.d.ts +0 -2
  865. package/dist/shell/page-bar.test.d.ts.map +0 -1
  866. package/dist/state/fields.test.d.ts +0 -2
  867. package/dist/state/fields.test.d.ts.map +0 -1
  868. package/dist/state/persisted.test.d.ts +0 -2
  869. package/dist/state/persisted.test.d.ts.map +0 -1
  870. package/dist/styles.floor.test.d.ts +0 -2
  871. package/dist/styles.floor.test.d.ts.map +0 -1
  872. package/dist/theme/border-coverage.test.d.ts +0 -18
  873. package/dist/theme/border-coverage.test.d.ts.map +0 -1
  874. package/dist/theme/contrast.test.d.ts +0 -2
  875. package/dist/theme/contrast.test.d.ts.map +0 -1
  876. package/dist/theme/ctl-theme.test.d.ts +0 -2
  877. package/dist/theme/ctl-theme.test.d.ts.map +0 -1
  878. package/dist/theme/ctl-tier-coverage.test.d.ts +0 -2
  879. package/dist/theme/ctl-tier-coverage.test.d.ts.map +0 -1
  880. package/dist/theme/density-option.test.d.ts +0 -2
  881. package/dist/theme/density-option.test.d.ts.map +0 -1
  882. package/dist/theme/density-relations.test.d.ts +0 -2
  883. package/dist/theme/density-relations.test.d.ts.map +0 -1
  884. package/dist/theme/derive-theme.test.d.ts +0 -2
  885. package/dist/theme/derive-theme.test.d.ts.map +0 -1
  886. package/dist/theme/divider-law.test.d.ts +0 -2
  887. package/dist/theme/divider-law.test.d.ts.map +0 -1
  888. package/dist/theme/fonts-option.test.d.ts +0 -2
  889. package/dist/theme/fonts-option.test.d.ts.map +0 -1
  890. package/dist/theme/icon-slot.test.d.ts +0 -2
  891. package/dist/theme/icon-slot.test.d.ts.map +0 -1
  892. package/dist/theme/layout-rhythm-css.test.d.ts +0 -2
  893. package/dist/theme/layout-rhythm-css.test.d.ts.map +0 -1
  894. package/dist/theme/radius-option.test.d.ts +0 -2
  895. package/dist/theme/radius-option.test.d.ts.map +0 -1
  896. package/dist/theme/radius.test.d.ts +0 -2
  897. package/dist/theme/radius.test.d.ts.map +0 -1
  898. package/dist/theme/shadow-surfaces.test.d.ts +0 -26
  899. package/dist/theme/shadow-surfaces.test.d.ts.map +0 -1
  900. package/dist/theme/spacing.test.d.ts +0 -2
  901. package/dist/theme/spacing.test.d.ts.map +0 -1
  902. package/dist/theme/use-basalt-spacing.test.d.ts +0 -2
  903. package/dist/theme/use-basalt-spacing.test.d.ts.map +0 -1
  904. package/dist/theme-lab/derive-controls.test.d.ts +0 -2
  905. package/dist/theme-lab/derive-controls.test.d.ts.map +0 -1
  906. package/dist/theme-lab/derive-state.test.d.ts +0 -2
  907. package/dist/theme-lab/derive-state.test.d.ts.map +0 -1
  908. package/dist/theme-lab/theme-lab.test.d.ts +0 -2
  909. package/dist/theme-lab/theme-lab.test.d.ts.map +0 -1
  910. package/dist/tokens/build-fonts-css.test.d.ts +0 -2
  911. package/dist/tokens/build-fonts-css.test.d.ts.map +0 -1
  912. package/dist/tokens/build-radius-css.test.d.ts +0 -2
  913. package/dist/tokens/build-radius-css.test.d.ts.map +0 -1
  914. package/dist/tokens/chart-margin.test.d.ts +0 -2
  915. package/dist/tokens/chart-margin.test.d.ts.map +0 -1
  916. package/dist/tokens/density.test.d.ts +0 -2
  917. package/dist/tokens/density.test.d.ts.map +0 -1
  918. package/dist/tokens/derive-radius.test.d.ts +0 -2
  919. package/dist/tokens/derive-radius.test.d.ts.map +0 -1
  920. package/dist/tokens/derive.test.d.ts +0 -2
  921. package/dist/tokens/derive.test.d.ts.map +0 -1
  922. package/dist/tokens/palette.test.d.ts +0 -2
  923. package/dist/tokens/palette.test.d.ts.map +0 -1
  924. package/dist/tokens/text-scale.test.d.ts +0 -2
  925. package/dist/tokens/text-scale.test.d.ts.map +0 -1
  926. package/dist/vite-color-mix.test.d.ts +0 -2
  927. package/dist/vite-color-mix.test.d.ts.map +0 -1
  928. package/dist/vite.test.d.ts +0 -2
  929. package/dist/vite.test.d.ts.map +0 -1
  930. package/dist/widget-header/delta-badge.test.d.ts +0 -2
  931. package/dist/widget-header/delta-badge.test.d.ts.map +0 -1
  932. package/dist/widget-header/no-mantine.test.d.ts +0 -2
  933. package/dist/widget-header/no-mantine.test.d.ts.map +0 -1
  934. package/dist/widget-header/widget-header.test.d.ts +0 -2
  935. package/dist/widget-header/widget-header.test.d.ts.map +0 -1
  936. package/src/agent/adapter.test.ts +0 -1640
  937. package/src/agent/ai-sdk-transport.test.ts +0 -658
  938. package/src/agent/coalesce.test.ts +0 -387
  939. package/src/agent/foreign.test.ts +0 -51
  940. package/src/agent/id.test.ts +0 -169
  941. package/src/agent/merge.test.ts +0 -206
  942. package/src/agent/part-list.test.tsx +0 -112
  943. package/src/agent/parts.test.ts +0 -720
  944. package/src/agent/thread.test.ts +0 -220
  945. package/src/agent/transport.test.ts +0 -70
  946. package/src/agent/use-agent-stream.test.tsx +0 -287
  947. package/src/agent/use-agent-thread-runs.foreign.test.tsx +0 -138
  948. package/src/agent/use-agent-thread-runs.resume.test.tsx +0 -294
  949. package/src/agent/use-agent-thread-runs.test.tsx +0 -1120
  950. package/src/agent/use-agent-thread-runs.wedge.test.tsx +0 -387
  951. package/src/agent-chat/composer.test.tsx +0 -924
  952. package/src/agent-chat/relative-time.test.ts +0 -95
  953. package/src/agent-chat/thread-feed-row.test.tsx +0 -510
  954. package/src/agent-chat/thread-feed.test.tsx +0 -241
  955. package/src/agent-chat/thread-message.test.tsx +0 -1285
  956. package/src/agent-chat/thread-workspace.test.tsx +0 -169
  957. package/src/agent-chat/tool-chip.test.tsx +0 -139
  958. package/src/agent-chat/virtualize.type-guard.test.ts +0 -49
  959. package/src/charts/cursor/resolve.test.ts +0 -151
  960. package/src/charts/cursor/store.test.ts +0 -64
  961. package/src/charts/kinds/BandStrip.test.tsx +0 -282
  962. package/src/charts/kinds/Bars.test.tsx +0 -183
  963. package/src/charts/kinds/DualPanel.test.tsx +0 -527
  964. package/src/charts/kinds/MirroredBars.test.tsx +0 -232
  965. package/src/charts/kinds/MultiLine.test.tsx +0 -218
  966. package/src/charts/kinds/StackedArea.test.tsx +0 -114
  967. package/src/charts/kinds/ZonedLine.test.tsx +0 -147
  968. package/src/charts/layout/auto-margin.test.ts +0 -42
  969. package/src/charts/primitives/CartesianChart.test.tsx +0 -647
  970. package/src/charts/primitives/ChartCard.test.tsx +0 -66
  971. package/src/charts/primitives/ChartFrame.test.tsx +0 -142
  972. package/src/charts/primitives/ChartLegend.test.tsx +0 -78
  973. package/src/charts/primitives/ChartTooltip.test.tsx +0 -44
  974. package/src/charts/primitives/XZoneRects.test.tsx +0 -123
  975. package/src/charts/series.test.ts +0 -112
  976. package/src/charts/utils/domain.test.ts +0 -54
  977. package/src/charts/utils/measure-text.test.ts +0 -36
  978. package/src/cli/check-coverage.test.ts +0 -297
  979. package/src/cli/check-theme.test.ts +0 -837
  980. package/src/cli/help.test.ts +0 -170
  981. package/src/cli/init-sync.test.ts +0 -343
  982. package/src/cli/placement-engine.test.ts +0 -298
  983. package/src/cli/shipped-versions.test.ts +0 -83
  984. package/src/cli/tarball-parity.test.ts +0 -44
  985. package/src/cli/template-format.test.ts +0 -64
  986. package/src/cli/template-ledger.test.ts +0 -219
  987. package/src/cli/tokens-css.test.ts +0 -249
  988. package/src/cli/toolchain-wiring.test.ts +0 -1358
  989. package/src/cli/waiver-audit.test.ts +0 -215
  990. package/src/content/article-actions.test.ts +0 -78
  991. package/src/content/article-model.test.ts +0 -119
  992. package/src/content/block-split.test.ts +0 -75
  993. package/src/content/heading-components.test.tsx +0 -65
  994. package/src/content/markdown.test.tsx +0 -603
  995. package/src/content/prose.module.css.test.ts +0 -39
  996. package/src/content/sanitize.test.ts +0 -659
  997. package/src/content/slug.test.ts +0 -62
  998. package/src/controls/actions.test.tsx +0 -252
  999. package/src/controls/actions.type-guard.test.ts +0 -63
  1000. package/src/controls/control-group.test.tsx +0 -229
  1001. package/src/controls/controls.router.test.tsx +0 -1053
  1002. package/src/controls/filter-set.test.tsx +0 -472
  1003. package/src/controls/peer-boundary.test.ts +0 -85
  1004. package/src/controls/props-contract.test.tsx +0 -137
  1005. package/src/controls/range-filter.type-guard.test.tsx +0 -122
  1006. package/src/controls/sync-button.test.tsx +0 -183
  1007. package/src/dashboard/query-state.test.tsx +0 -152
  1008. package/src/dashboard/settings-section.test.tsx +0 -56
  1009. package/src/dashboard/stat-card.test.tsx +0 -385
  1010. package/src/data/data-table.test.tsx +0 -434
  1011. package/src/guard/check-source.test.ts +0 -3388
  1012. package/src/guard/grace.test.ts +0 -158
  1013. package/src/guard/guard-hook.test.ts +0 -203
  1014. package/src/provider/index.test.tsx +0 -101
  1015. package/src/provider/lab-theme.test.ts +0 -147
  1016. package/src/query/error-message.test.ts +0 -67
  1017. package/src/router-tanstack/multi-search-param-store.test.ts +0 -124
  1018. package/src/router-tanstack/nav.test.ts +0 -111
  1019. package/src/router-tanstack/search-param-store.test.ts +0 -135
  1020. package/src/router-tanstack/search-store.router.test.tsx +0 -882
  1021. package/src/router-tanstack/search-store.test.ts +0 -578
  1022. package/src/router-tanstack/use-nav.test.tsx +0 -187
  1023. package/src/section/section.test.tsx +0 -204
  1024. package/src/shell/app-mobile-nav.test.tsx +0 -403
  1025. package/src/shell/app-sidebar.test.tsx +0 -509
  1026. package/src/shell/index.test.tsx +0 -428
  1027. package/src/shell/mobile-nav-model.test.ts +0 -517
  1028. package/src/shell/page-bar.test.tsx +0 -316
  1029. package/src/state/fields.test.tsx +0 -512
  1030. package/src/state/persisted.test.ts +0 -91
  1031. package/src/styles.floor.test.ts +0 -36
  1032. package/src/theme/border-coverage.test.ts +0 -272
  1033. package/src/theme/contrast.test.ts +0 -354
  1034. package/src/theme/ctl-theme.test.tsx +0 -275
  1035. package/src/theme/ctl-tier-coverage.test.ts +0 -170
  1036. package/src/theme/density-option.test.ts +0 -96
  1037. package/src/theme/density-relations.test.ts +0 -506
  1038. package/src/theme/derive-theme.test.ts +0 -101
  1039. package/src/theme/divider-law.test.ts +0 -259
  1040. package/src/theme/fonts-option.test.ts +0 -75
  1041. package/src/theme/icon-slot.test.tsx +0 -275
  1042. package/src/theme/layout-rhythm-css.test.ts +0 -72
  1043. package/src/theme/radius-option.test.ts +0 -86
  1044. package/src/theme/radius.test.ts +0 -142
  1045. package/src/theme/shadow-surfaces.test.ts +0 -488
  1046. package/src/theme/spacing.test.ts +0 -495
  1047. package/src/theme/use-basalt-spacing.test.tsx +0 -55
  1048. package/src/theme-lab/derive-controls.test.ts +0 -94
  1049. package/src/theme-lab/derive-state.test.ts +0 -71
  1050. package/src/theme-lab/theme-lab.test.ts +0 -46
  1051. package/src/tokens/build-fonts-css.test.ts +0 -52
  1052. package/src/tokens/build-radius-css.test.ts +0 -25
  1053. package/src/tokens/chart-margin.test.ts +0 -39
  1054. package/src/tokens/density.test.ts +0 -259
  1055. package/src/tokens/derive-radius.test.ts +0 -62
  1056. package/src/tokens/derive.test.ts +0 -30
  1057. package/src/tokens/palette.test.ts +0 -114
  1058. package/src/tokens/text-scale.test.ts +0 -51
  1059. package/src/vite-color-mix.test.ts +0 -46
  1060. package/src/vite.test.ts +0 -423
  1061. package/src/widget-header/delta-badge.test.tsx +0 -60
  1062. package/src/widget-header/no-mantine.test.ts +0 -33
  1063. package/src/widget-header/widget-header.test.tsx +0 -187
@@ -0,0 +1,392 @@
1
+ /**
2
+ * `PanelRow` — the inspector/facet ROW every control renders on the `panel` surface, and on the
3
+ * mobile `sheet` surface too (`docs/ASIDE-SPEC.md` §1 "Inspector", §3; `docs/CONTROLS-SPEC.md` §3:
4
+ * "sheet = panel rows inside a Drawer") — a labelled block around one control, and a HOME: it wraps
5
+ * its slots in `CtlSlot`, so a control inside carries no `size` of its own (law C5).
6
+ *
7
+ * **Label above, never beside (G4).** At an aside's ~300px, label · control · readout on one line
8
+ * leaves the control about 90px — under 12px per step for a 20-step slider, which is why the CBBI
9
+ * page hand-rolled two-line rows before this existed. The ONE exception is `end`: a control that
10
+ * rides the label line because it is atomic and needs no width (a `Switch`).
11
+ *
12
+ * The skeleton is plain elements plus one CSS module — no Mantine `Group`/`Stack`. A row is drawn
13
+ * dozens of times in one column, and the hairline/inset law it carries (`.row + .row`) is a
14
+ * SIBLING relationship, which a `Stack`'s gap cannot express.
15
+ *
16
+ * @example
17
+ * <PanelRow label="Pi Cycle Top" hint="The 111DMA / 350DMA×2 crossover." readout="0.62">
18
+ * <SliderControl field={weights.field.piCycle} label="Pi Cycle Top" />
19
+ * </PanelRow>
20
+ *
21
+ * @example
22
+ * // A toggle: one line, the control on the label line, no control line at all.
23
+ * <PanelRow label="Reweighted" end={<Switch checked={on} onChange={…} />} />
24
+ */
25
+ import { SegmentedControl, Select } from '@mantine/core'
26
+ import { useLayoutEffect, useRef, useState } from 'react'
27
+ import type { ReactNode, RefObject } from 'react'
28
+ import { cx } from '../common/props'
29
+ import type { BasaltProps } from '../common/props'
30
+ import { CtlSlot } from '../theme'
31
+ import { InfoGlyph } from '../widget-header/widget-header'
32
+ import classes from './panel-row.module.css'
33
+
34
+ /**
35
+ * Past three options a panel row's choice is a `Select`, not a track. Same arithmetic as
36
+ * `ViewTabs`' phone form, against the aside's ~300px rather than a phone's: a four-segment track at
37
+ * that width gives each label ~60px, which truncates every word longer than "Previous".
38
+ *
39
+ * **The count, not the whole law.** Three options at three WORDS' worth of label is not the same
40
+ * claim as three options at three SENTENCES' worth — "Same period last year" clipped mid-word
41
+ * inside its equal third even at a set of exactly three. `PANEL_TRACK_MAX` is the cheap first gate;
42
+ * `useTrackFits` (below) is the actual width check that decides whether the track renders at all.
43
+ */
44
+ export const PANEL_TRACK_MAX = 3
45
+
46
+ export type PanelRowProps = BasaltProps & {
47
+ /** The row's name. Rendered above the control, `xs`/550 — never inside it. */
48
+ readonly label: string
49
+ /** Info glyph + tooltip beside the label — the same affordance `WidgetHeader.info` draws. */
50
+ readonly hint?: string
51
+ /**
52
+ * The current value, mono and right-aligned on the label line. A slider's readout, a range's
53
+ * resolved window — anything the control itself does not print.
54
+ */
55
+ readonly readout?: ReactNode
56
+ /**
57
+ * A control that rides the LABEL line instead of taking one of its own — a `Switch`, and by
58
+ * design almost nothing else. Anything with a width belongs in `children`.
59
+ */
60
+ readonly end?: ReactNode
61
+ /** Dims the row. The control inside keeps its own `disabled` — this is the visual half. */
62
+ readonly disabled?: boolean
63
+ /**
64
+ * Stamped on the label so the control can point `aria-labelledby` at it. The CONTROL owns the id
65
+ * (it renders this row), so an id published downward could never reach it — see
66
+ * `useControlName`'s doc.
67
+ */
68
+ readonly labelId?: string
69
+ /**
70
+ * When set, renders the label as a `<label htmlFor={htmlFor}>` instead of a bare `<span>`, so
71
+ * clicking the label text also activates the control — the affordance Mantine's own labelled
72
+ * inputs give for free. Pass the `end` control's id (a `Switch`, typically).
73
+ */
74
+ readonly htmlFor?: string
75
+ /** The full-width control line. Omitted for a row whose control rides `end`. */
76
+ readonly children?: ReactNode
77
+ }
78
+
79
+ export function PanelRow({
80
+ label,
81
+ hint,
82
+ readout,
83
+ end,
84
+ disabled,
85
+ labelId,
86
+ htmlFor,
87
+ children,
88
+ className,
89
+ style,
90
+ }: PanelRowProps): ReactNode {
91
+ return (
92
+ <div
93
+ className={cx(classes.row, className)}
94
+ {...(disabled === true && { 'data-disabled': true })}
95
+ {...(style !== undefined && { style })}
96
+ >
97
+ {/* ONE slot for the whole row, not one per line: `CtlSlot`'s marker is `display: contents`,
98
+ so both lines stay flex children of `.row` and the tier reaches `end` and `children`
99
+ through a single provider. */}
100
+ <CtlSlot>
101
+ <div className={classes.head}>
102
+ {htmlFor !== undefined ? (
103
+ <label
104
+ htmlFor={htmlFor}
105
+ className={classes.label}
106
+ {...(labelId !== undefined && { id: labelId })}
107
+ >
108
+ {label}
109
+ </label>
110
+ ) : (
111
+ <span className={classes.label} {...(labelId !== undefined && { id: labelId })}>
112
+ {label}
113
+ </span>
114
+ )}
115
+ {hint !== undefined && <InfoGlyph text={hint} />}
116
+ {readout !== undefined && <span className={classes.readout}>{readout}</span>}
117
+ {end !== undefined && <span className={classes.end}>{end}</span>}
118
+ </div>
119
+ {children !== undefined && <div className={classes.control}>{children}</div>}
120
+ </CtlSlot>
121
+ </div>
122
+ )
123
+ }
124
+
125
+ /** One option as both surfaces render it — `disabled` is the `SegmentedControl` half. */
126
+ export type PanelChoiceOption = {
127
+ readonly value: string
128
+ readonly label: string
129
+ readonly disabled?: boolean
130
+ }
131
+
132
+ export type PanelChoiceProps = {
133
+ /** The control's accessible name, as `useControlName` resolved it for this surface. */
134
+ readonly nameProps: { 'aria-labelledby': string } | { 'aria-label': string }
135
+ readonly value: string
136
+ readonly options: readonly PanelChoiceOption[]
137
+ /** Numeric labels (`7d` / `30d`) get the mono treatment `segmented-control.module.css` owns. */
138
+ readonly numeric?: boolean
139
+ readonly onChange: (next: string) => void
140
+ }
141
+
142
+ /**
143
+ * The nearest ANCESTOR that actually occupies layout space — skipping every `display: contents`
144
+ * box on the way up. Every home in this package wraps its slot in `CtlSlot` (`theme/ctl-theme.tsx`)
145
+ * to reach the `ctl` tier, and `CtlSlot`'s own marker is a `display: contents` `Box` (deliberately —
146
+ * it must stay out of the slot's own flex/grid layout). A `display: contents` element generates NO
147
+ * box of its own, so its `clientWidth` is always `0` — reading `root.parentElement.clientWidth`
148
+ * directly would report "zero room" for every control mounted straight into a slot (`ViewTabs`'
149
+ * desktop form, `PageBar`'s `actions`), which is not the constraint being measured at all.
150
+ */
151
+ function layoutParent(el: HTMLElement): HTMLElement | null {
152
+ let parent = el.parentElement
153
+ while (parent !== null && getComputedStyle(parent).display === 'contents') {
154
+ parent = parent.parentElement
155
+ }
156
+ return parent
157
+ }
158
+
159
+ /**
160
+ * A `SegmentedControl`'s own semantic class for a rendered option's label (`__staticSelector`,
161
+ * present on every Mantine styles-api part regardless of the CSS-module hash) — stable across a
162
+ * version bump the way a generated `_label_1pq5d_5` class is not.
163
+ */
164
+ const TRACK_LABEL_SELECTOR = '.mantine-SegmentedControl-label'
165
+
166
+ /**
167
+ * The authoritative check, meaningful only while the track is actually mounted — TWO signals, not
168
+ * one, because they catch two DIFFERENT ways a track fails to fit and neither subsumes the other:
169
+ *
170
+ * 1. **Root vs parent** (`root.offsetWidth > parent.clientWidth`) — the track's OWN root refuses to
171
+ * shrink below `min-width: max-content` (`theme/index.ts`'s `SegmentedControl.extend`), so a
172
+ * too-narrow ANCESTOR (a header's `actions` slot squeezing a non-`fullWidth` two-option track)
173
+ * shows up as the root overflowing its container.
174
+ * 2. **Per-label overflow** (`label.scrollWidth > label.clientWidth`) — the ctl-tier `fullWidth`
175
+ * label carries `width: 100%` (`theme/index.ts`, so it fills its equal `.control` share for
176
+ * ellipsis to work at all — `PanelChoice`'s doc). A `width: 100%` CHILD cannot contribute an
177
+ * intrinsic size to its ancestor's max-content computation (percentages are undefined until the
178
+ * container's own size is known, so the browser treats it as contributing ~nothing) — which is
179
+ * why `min-width: max-content` on the root measurably FAILS to grow past its available space at
180
+ * the phone tier: "Week"/"Absolute" clipped inside their equal column ("Wee", "Absolut") while
181
+ * `root.offsetWidth` still read as fitting its parent. Signal 1 cannot see this at all; only
182
+ * reading each rendered label directly can.
183
+ *
184
+ * Both signals require REAL layout, so `unknown` (parent not yet settled) takes priority over
185
+ * either — see `useTrackFits`'s own doc for why a `0` reading is not evidence of overflow.
186
+ */
187
+ function measureMounted(root: HTMLElement, parent: HTMLElement): 'fits' | 'overflow' | 'unknown' {
188
+ if (parent.clientWidth === 0) return 'unknown'
189
+ if (root.offsetWidth > parent.clientWidth) return 'overflow'
190
+ const labels = root.querySelectorAll<HTMLElement>(TRACK_LABEL_SELECTOR)
191
+ for (const label of labels) {
192
+ if (label.scrollWidth > label.clientWidth) return 'overflow'
193
+ }
194
+ return 'fits'
195
+ }
196
+
197
+ /**
198
+ * Fit-checks a full-width track: {@link PANEL_TRACK_MAX} (and `ViewTabs`' own `PHONE_TRACK_MAX`) is
199
+ * the cheap COUNT gate, not the whole law — three short labels and three long ones are not the same
200
+ * claim at a ~300px column or a phone sheet ("Same period last year" clipped mid-word inside its
201
+ * equal third even though the set was exactly three; "Week"/"Absolute" clipped inside their equal
202
+ * column at the phone tier even though the ROOT read as fitting its parent — see
203
+ * {@link measureMounted}'s doc for why that needs a second signal). This hook is the width gate
204
+ * underneath the count cap, shared so `PanelChoice` and `ViewTabs`' phone form cannot drift the way
205
+ * their two hand-rolled ternaries already had once (`PanelChoice`'s own doc).
206
+ *
207
+ * **A LIVE toggle, not a one-way latch — it reverts.** The moment the track overflows, `PanelChoice`
208
+ * swaps it for a `Select`, which un-mounts the only element `measureMounted` could otherwise read.
209
+ * Two refs survive that swap on purpose: `trackWidthRef` remembers the track's own last-measured
210
+ * width (refreshed live whenever it IS mounted, used only as a cheap pre-filter — see below — never
211
+ * as the final answer), and `parentRef` holds the layout parent found once, at the FIRST mount — the
212
+ * same DOM node hosts either form (`PanelChoice`/`ViewTabs` return one or the other as their sole
213
+ * element, never a wrapper that differs between them), so it is never itself removed by the swap.
214
+ * The `ResizeObserver` therefore never watches a node this hook is about to lose: it observes the
215
+ * PARENT for the whole hook's lifetime and the TRACK only while it exists.
216
+ *
217
+ * While `Select` is showing, an observer tick with room to spare (`parent.clientWidth` at or past
218
+ * the remembered `trackWidthRef`) SPECULATIVELY re-mounts the track rather than trying to predict
219
+ * the exact threshold from a cached number — the two-signal check above cannot be run without a
220
+ * live root, and a WRONG guess here is not user-visible: the second effect below re-verifies with
221
+ * `measureMounted` the instant the fresh root exists, inside the SAME `useLayoutEffect` pass, and
222
+ * reverts synchronously before paint if the speculation was wrong. Nothing here can loop — a
223
+ * `ResizeObserver` only fires again on a GENUINE subsequent size change, not because React
224
+ * re-rendered.
225
+ *
226
+ * A SECOND, dependency-free `useLayoutEffect` is what performs that verification (and, on the
227
+ * ordinary growth-first-detected-while-mounted path, is also what re-arms the observer on the
228
+ * track's own root once it exists) — the FIRST effect only runs once per `[countFits, dataKey]`,
229
+ * but the track itself mounts and unmounts every time `fits` changes, and a `ResizeObserver` has to
230
+ * be told about each new node explicitly; this second effect runs after every render and is a
231
+ * no-op unless `rootRef.current` is a node it is not already watching.
232
+ *
233
+ * The check is THREE-VALUED, not two — `unknown` alongside `fits`/`overflow` — because a
234
+ * `clientWidth` of `0` is not evidence of overflow, it is evidence the ancestor chain has not been
235
+ * LAID OUT yet: the aside animates its width in from 0 while `PageAside` claims the region
236
+ * (`docs/ASIDE-SPEC.md` §0), and every ancestor up to the aside panel itself reads `0` for one or
237
+ * more layout passes while that transition is in flight. `unknown` updates nothing — it is simply
238
+ * not yet an answer, and the observer keeps watching for the ancestor chain to settle.
239
+ *
240
+ * `useLayoutEffect` runs BEFORE the browser paints, so a CONFIDENT overflow reading is never
241
+ * actually shown as a track first: the synchronous `setState` for it lands before paint, not after.
242
+ *
243
+ * `dataKey` is a caller-built string identifying the CURRENT option set (not read inside the effect
244
+ * body, just what forces it to re-run — a fresh mount, a fresh `parentRef`/`trackWidthRef` — when
245
+ * the rendered labels change) — primitive on purpose, so it is an honest `useLayoutEffect`
246
+ * dependency rather than a fresh array reference on every render.
247
+ */
248
+ export function useTrackFits(
249
+ countFits: boolean,
250
+ dataKey: string,
251
+ ): { rootRef: RefObject<HTMLDivElement | null>; fits: boolean } {
252
+ const rootRef = useRef<HTMLDivElement>(null)
253
+ const [rootFits, setRootFits] = useState(true)
254
+ const observerRef = useRef<ResizeObserver | null>(null)
255
+ const parentRef = useRef<HTMLElement | null>(null)
256
+ const trackWidthRef = useRef(0)
257
+ const observedRootRef = useRef<HTMLElement | null>(null)
258
+
259
+ useLayoutEffect(() => {
260
+ if (!countFits) return
261
+ const root = rootRef.current
262
+ if (root === null) return
263
+ const parent = layoutParent(root)
264
+ if (parent === null) return
265
+ parentRef.current = parent
266
+ trackWidthRef.current = root.offsetWidth
267
+
268
+ const resolve = (): void => {
269
+ const p = parentRef.current
270
+ if (p === null) return
271
+ const liveRoot = rootRef.current
272
+
273
+ if (liveRoot !== null) {
274
+ const result = measureMounted(liveRoot, p)
275
+ trackWidthRef.current = liveRoot.offsetWidth
276
+ if (result === 'unknown') return
277
+ const fits = result === 'fits'
278
+ setRootFits((current) => (current === fits ? current : fits))
279
+ return
280
+ }
281
+
282
+ // `Select` is mounted — speculate rather than measure (see this hook's own doc). No-op
283
+ // while shrinking or unmeasured: only an actual increase past the last known track width is
284
+ // worth a remount attempt.
285
+ if (p.clientWidth === 0 || trackWidthRef.current === 0) return
286
+ if (p.clientWidth >= trackWidthRef.current) setRootFits(true)
287
+ }
288
+
289
+ if (typeof ResizeObserver === 'undefined') {
290
+ // No recovery path exists without an observer, so a same-tick confident read is the only
291
+ // chance this environment gets.
292
+ resolve()
293
+ return
294
+ }
295
+
296
+ const observer = new ResizeObserver(resolve)
297
+ observerRef.current = observer
298
+ observer.observe(parent)
299
+ observer.observe(root)
300
+ observedRootRef.current = root
301
+
302
+ // Resolve once, synchronously, so a track that is ALREADY confidently measurable (its
303
+ // ancestors settled before this component even mounted — the common case) decides before
304
+ // paint instead of waiting for the observer's first async tick.
305
+ resolve()
306
+
307
+ return () => {
308
+ observer.disconnect()
309
+ observerRef.current = null
310
+ observedRootRef.current = null
311
+ }
312
+ }, [countFits, dataKey])
313
+
314
+ // Re-arms the SAME observer on a fresh track root whenever one (re)mounts, AND is what actually
315
+ // VERIFIES a speculative remount (see this hook's own doc) — a wrong guess reverts here,
316
+ // synchronously, before this render ever paints. A no-op on every render where the track is
317
+ // absent (`Select` showing) or already the node being watched — deliberately NO dependency
318
+ // array, since a `ResizeObserver` has to be told about each fresh root explicitly and a fresh
319
+ // root can appear on any render. Cannot loop: every `setRootFits` call below is a functional
320
+ // update gated on the value actually CHANGING, so a render this effect does not alter never
321
+ // re-triggers it.
322
+ // oxlint-disable-next-line react-hooks/exhaustive-deps -- deliberately runs every render, see above
323
+ useLayoutEffect(() => {
324
+ const observer = observerRef.current
325
+ const root = rootRef.current
326
+ const parent = parentRef.current
327
+ if (observer === null || root === null || observedRootRef.current === root) return
328
+ if (observedRootRef.current !== null) observer.unobserve(observedRootRef.current)
329
+ observer.observe(root)
330
+ observedRootRef.current = root
331
+ trackWidthRef.current = root.offsetWidth
332
+
333
+ if (parent === null) return
334
+ const result = measureMounted(root, parent)
335
+ if (result === 'unknown') return
336
+ const fits = result === 'fits'
337
+ setRootFits((current) => (current === fits ? current : fits))
338
+ })
339
+
340
+ return { rootRef, fits: countFits && rootFits }
341
+ }
342
+
343
+ /**
344
+ * The single-choice control a panel row draws: a full-width track while the set fits both
345
+ * {@link PANEL_TRACK_MAX} and the width check {@link useTrackFits} runs, a `Select` past either.
346
+ *
347
+ * Internal, and one component rather than the same ternary in each control: `EnumFilter` and
348
+ * `ViewTabs` both wrote it out and had already drifted — `ViewTabs`' copy lost `data-numeric`, so a
349
+ * numeric tab set rendered proportional in the aside and mono everywhere else. The split point is
350
+ * the geometry of a ~300px column, which is a property of the ROW, not of any one filter, so it
351
+ * belongs beside the row that imposes it.
352
+ */
353
+ export function PanelChoice({
354
+ nameProps,
355
+ value,
356
+ options,
357
+ numeric,
358
+ onChange,
359
+ }: PanelChoiceProps): ReactNode {
360
+ const data = options.map((option) => ({
361
+ value: option.value,
362
+ label: option.label,
363
+ disabled: option.disabled === true,
364
+ }))
365
+ const countFits = options.length <= PANEL_TRACK_MAX
366
+ const { rootRef, fits } = useTrackFits(countFits, data.map((d) => d.value).join('|'))
367
+
368
+ if (fits) {
369
+ return (
370
+ <SegmentedControl
371
+ ref={rootRef}
372
+ {...nameProps}
373
+ fullWidth
374
+ value={value}
375
+ data={data}
376
+ {...(numeric === true && { 'data-numeric': true })}
377
+ onChange={onChange}
378
+ />
379
+ )
380
+ }
381
+ return (
382
+ <Select
383
+ {...nameProps}
384
+ value={value}
385
+ allowDeselect={false}
386
+ data={data}
387
+ onChange={(next) => {
388
+ if (next !== null) onChange(next)
389
+ }}
390
+ />
391
+ )
392
+ }
@@ -10,11 +10,12 @@
10
10
  * `theme/segmented-control.module.css` owns; that attribute is what retired the per-consumer
11
11
  * `theme-allow` + inline `fontFamily` hack (C7).
12
12
  *
13
- * In the SHEET they render as a `SheetOptionList`, and the custom picker sits behind a
14
- * `Custom range…` disclosure row. A vertical `SegmentedControl` stretched across a bottom drawer
15
- * read as a broken control rather than a choice (see `SheetOptionList`'s doc), and an unconditional
16
- * calendar pushed every OTHER filter in the sheet below the fold — the sheet holds all of them, and
17
- * `RangeFilter` is one.
13
+ * In the PANEL and the SHEET (`docs/CONTROLS-SPEC.md` §3: "sheet = panel rows inside a Drawer")
14
+ * they render as one `Select` — label above, the presets plus a `Custom range…` ROW inside the same
15
+ * list, which reveals the SAME injected picker underneath. A track cannot hold five date presets at
16
+ * ~300px (or across a bottom drawer, where a vertical `SegmentedControl` read as a broken control
17
+ * rather than a choice), and a second control beside the choice would cost the column — or an
18
+ * unconditional calendar pushed below every OTHER filter in the sheet — a row it does not have.
18
19
  *
19
20
  * The custom picker is INJECTED, never imported: `basalt-ui/controls-dates` holds the
20
21
  * `@mantine/dates` implementation, and this module must resolve for a consumer who has no
@@ -26,27 +27,28 @@
26
27
  * // range: field.range({ presets: ['7d', '30d', '90d', 'ytd'], fallback: '30d', custom: true })
27
28
  * <RangeFilter field={analytics.field.range} customPicker={DateRangePicker} />
28
29
  */
29
- import { SegmentedControl, Stack } from '@mantine/core'
30
- import { useState } from 'react'
30
+ import { SegmentedControl, Select, Stack } from '@mantine/core'
31
+ import { useEffect, useRef, useState } from 'react'
31
32
  import type { ComponentType, ReactNode } from 'react'
33
+ import type { BasaltProps } from '../common/props'
34
+ import { assertRequiredProps } from '../common/validate'
35
+ import { isDev } from '../utils/is-dev'
32
36
  import type { FieldHandle, RangeField, RangeValue } from '../state'
33
37
  import classes from './controls.module.css'
34
38
  import { useFilterRegistration, useFilterSurface } from './filter-context'
35
39
  import { FilterPill } from './filter-pill'
36
40
  import { CalendarGlyph } from './glyphs'
37
- import { SheetDisclosure, SheetField, SheetOptionList, useControlName } from './filter-sheet'
41
+ import { useControlName } from './filter-sheet'
42
+ import { PanelRow } from './panel-row'
38
43
 
39
44
  /** Past four presets the track goes vertical. */
40
45
  const VERTICAL_FROM = 5
46
+ /** The panel `Select`'s last row — the same wording the sheet's disclosure uses, and the same
47
+ * `'custom'` preset value the field already declares, so no synthetic option value exists. */
48
+ const CUSTOM_ROW_LABEL = 'Custom range…'
41
49
  /** `7d`, `30d`, `24h`, `90` — a label the mono numeric treatment is for. */
42
50
  const NUMERIC_LABEL = /^\d/
43
51
 
44
- /** The house dev gate — `basaltViteConfig` defines `process.env.NODE_ENV`, so a production bundle
45
- * constant-folds this to `false` and drops the warning. Read per call, never hoisted. */
46
- function isDev(): boolean {
47
- return process.env['NODE_ENV'] !== 'production'
48
- }
49
-
50
52
  /** Once per label — a filter renders on every navigation, and the wiring is fixed at definition. */
51
53
  const noPickerWarned = new Set<string>()
52
54
 
@@ -80,7 +82,7 @@ export type RangeCustomPickerProps = {
80
82
  }
81
83
 
82
84
  /** Everything about the props that does not depend on the field's `custom` flag. */
83
- type RangeFilterBase<P extends string, C extends boolean> = {
85
+ type RangeFilterBase<P extends string, C extends boolean> = BasaltProps & {
84
86
  /**
85
87
  * A range handle from either store factory. Generic over the field's `custom` flag so a
86
88
  * `field.range({ ... })` WITHOUT `custom` (whose handle is `RangeField<P, false>`, and whose
@@ -110,11 +112,18 @@ export type RangeFilterProps<P extends string, C extends boolean = boolean> = Ra
110
112
  export function RangeFilter<P extends string, C extends boolean = boolean>(
111
113
  props: RangeFilterProps<P, C>,
112
114
  ): ReactNode {
115
+ // F-ERR-1 — without this a missing `field` surfaces as `undefined is not an object
116
+ // (evaluating 'field.use')`, caught by `BasaltErrorBoundary`.
117
+ assertRequiredProps('RangeFilter', props, ['field'], {
118
+ field: 'bind it to a store field (`store.field.<name>`), never a value/onChange pair.',
119
+ })
113
120
  const {
114
121
  field,
115
122
  icon,
116
123
  customPicker,
117
124
  label = 'Range',
125
+ className,
126
+ style,
118
127
  // One cast, at the one boundary a generic `C` cannot be read through: `C` only ever describes
119
128
  // the FIELD's custom flag, and the body handles both.
120
129
  } = props as unknown as RangeFilterBase<P, boolean> & {
@@ -132,49 +141,75 @@ export function RangeFilter<P extends string, C extends boolean = boolean>(
132
141
  const allowsCustom = options.length !== presets.length
133
142
  const numeric = presets.length > 0 && presets.every((option) => NUMERIC_LABEL.test(option.label))
134
143
  const inSheet = surface === 'sheet'
144
+ const inPanel = surface === 'panel'
135
145
  const Picker = customPicker
136
146
  warnCustomWithoutPicker(label, allowsCustom && Picker === undefined)
137
147
  // The track is a `radiogroup`; without this it announces unnamed, and the pill it hangs off reads
138
148
  // the VALUE (`30d`), not the filter.
139
- const { labelId, nameProps } = useControlName(label, inSheet)
149
+ const { labelId, nameProps } = useControlName(label, inSheet || inPanel)
140
150
  // Expanded when the field ALREADY holds a custom window — a reader who deep-linked one must see
141
151
  // it without hunting for the row that holds it. Local, because it is an overlay's open flag and
142
152
  // not a filter value (the same line `FilterPill`'s `opened` state draws).
143
153
  const [customOpen, setCustomOpen] = useState(value.preset === 'custom')
154
+ // …and RE-SYNCED whenever the preset moves under it. Seeded once, the flag survived a write this
155
+ // control did not make: a `Reset all` or a `field.clear()` from a `FilterSet` put the field back
156
+ // on `30d` while the panel's Select still read `Custom range…` with the picker open beneath it.
157
+ // Tracked against the LAST SEEN preset (`search-filter.tsx`'s `useDebouncedField` idiom) rather
158
+ // than against `value.preset` alone, because the just-clicked `Custom range…` transient is
159
+ // exactly the state where the flag is true and the preset has not moved yet.
160
+ const seenPreset = useRef(value.preset)
161
+ useEffect(() => {
162
+ if (seenPreset.current === value.preset) return
163
+ seenPreset.current = value.preset
164
+ setCustomOpen(value.preset === 'custom')
165
+ }, [value.preset])
144
166
  const showPicker = allowsCustom && Picker !== undefined
145
167
 
146
- if (inSheet) {
168
+ // The sheet form is the panel form (`docs/CONTROLS-SPEC.md` §3: "sheet = panel rows inside a
169
+ // Drawer") — one `Select` holding the presets plus a `Custom range…` row, which reveals the same
170
+ // injected picker underneath. It used to be a `SheetOptionList` with the picker behind a separate
171
+ // `SheetDisclosure` row; the `Select` already folds both into one control, so the disclosure had
172
+ // no job left.
173
+ if (inPanel || inSheet) {
147
174
  return (
148
- <SheetField label={label} labelId={labelId}>
149
- <SheetOptionList
150
- mode="single"
151
- labelId={labelId}
152
- selected={[value.preset]}
153
- options={presets.map((option) => ({ value: option.value, label: option.label }))}
154
- onToggle={(next) => {
175
+ <PanelRow
176
+ label={label}
177
+ labelId={labelId}
178
+ {...(className !== undefined && { className })}
179
+ {...(style !== undefined && { style })}
180
+ >
181
+ <Select
182
+ {...nameProps}
183
+ allowDeselect={false}
184
+ // `customOpen` is what the row reads while the picker is open but no window has been
185
+ // picked yet — selecting `Custom range…` reveals the calendar, it does not write a
186
+ // half-built `{ preset: 'custom' }` the codec would have to guess at.
187
+ value={customOpen ? 'custom' : value.preset}
188
+ data={[
189
+ ...presets.map((option) => ({ value: option.value, label: option.label })),
190
+ ...(showPicker ? [{ value: 'custom', label: CUSTOM_ROW_LABEL }] : []),
191
+ ]}
192
+ onChange={(next) => {
193
+ if (next === null) return
194
+ if (next === 'custom') {
195
+ setCustomOpen(true)
196
+ return
197
+ }
155
198
  setCustomOpen(false)
156
199
  // Rendered from `field.options`, so `next` is always a declared preset — the cast
157
200
  // restores what the codec already guarantees.
158
201
  setValue({ preset: next } as RangeValue<P | 'custom'>)
159
202
  }}
160
203
  />
161
- {showPicker && (
162
- <SheetDisclosure
163
- label="Custom range…"
164
- expanded={customOpen}
165
- onToggle={() => {
166
- setCustomOpen((open) => !open)
204
+ {showPicker && customOpen && (
205
+ <Picker
206
+ value={{ from: value.from, to: value.to }}
207
+ onChange={({ from, to }) => {
208
+ setValue({ preset: 'custom', from, to } as RangeValue<P | 'custom'>)
167
209
  }}
168
- >
169
- <Picker
170
- value={{ from: value.from, to: value.to }}
171
- onChange={({ from, to }) => {
172
- setValue({ preset: 'custom', from, to } as RangeValue<P | 'custom'>)
173
- }}
174
- />
175
- </SheetDisclosure>
210
+ />
176
211
  )}
177
- </SheetField>
212
+ </PanelRow>
178
213
  )
179
214
  }
180
215
 
@@ -210,6 +245,8 @@ export function RangeFilter<P extends string, C extends boolean = boolean>(
210
245
  active={!isDefault}
211
246
  numeric={numeric && value.preset !== 'custom'}
212
247
  icon={icon ?? <CalendarGlyph />}
248
+ {...(className !== undefined && { className })}
249
+ {...(style !== undefined && { style })}
213
250
  >
214
251
  <div className={classes.optionList}>{body}</div>
215
252
  </FilterPill>
@@ -15,25 +15,30 @@
15
15
  import { TextInput } from '@mantine/core'
16
16
  import { useEffect, useRef, useState } from 'react'
17
17
  import type { ReactNode } from 'react'
18
+ import type { BasaltProps } from '../common/props'
19
+ import { assertRequiredProps } from '../common/validate'
18
20
  import type { FieldHandle, StringField } from '../state'
19
21
  import { useFilterRegistration, useFilterSurface } from './filter-context'
20
- import { SheetField, useControlName } from './filter-sheet'
22
+ import { useControlName } from './filter-sheet'
23
+ import { PanelRow } from './panel-row'
21
24
 
22
25
  /** One navigation per typed phrase, not per keystroke. */
23
26
  const DEBOUNCE_MS = 200
24
27
 
25
- export type SearchFilterProps = {
28
+ export type SearchFilterProps = BasaltProps & {
26
29
  readonly field: FieldHandle<StringField>
27
30
  readonly placeholder?: string
28
31
  /** Sheet-form heading. @default 'Search' */
29
32
  readonly label?: string
30
33
  }
31
34
 
32
- export function SearchFilter({
33
- field,
34
- placeholder,
35
- label = 'Search',
36
- }: SearchFilterProps): ReactNode {
35
+ export function SearchFilter(props: SearchFilterProps): ReactNode {
36
+ // F-ERR-1 — without this a missing `field` surfaces as `undefined is not an object
37
+ // (evaluating 'field.use')`, caught by `BasaltErrorBoundary`.
38
+ assertRequiredProps('SearchFilter', props, ['field'], {
39
+ field: 'bind it to a store field (`store.field.<name>`), never a value/onChange pair.',
40
+ })
41
+ const { field, placeholder, label = 'Search', className, style } = props
37
42
  const [value, setValue] = field.use()
38
43
  const surface = useFilterSurface()
39
44
  const draft = useDebouncedField(value, setValue)
@@ -42,8 +47,12 @@ export function SearchFilter({
42
47
  })
43
48
 
44
49
  // `placeholder` is optional and, even when set, stops being the name the moment the user types —
45
- // so the name comes from `label`, or from the sheet heading when there is one.
46
- const { labelId, nameProps } = useControlName(label, surface === 'sheet')
50
+ // so the name comes from `label`, or from the visible heading when the surface draws one.
51
+ const named = surface === 'sheet' || surface === 'panel'
52
+ const { labelId, nameProps } = useControlName(label, named)
53
+ // The pill form has no wrapping home — the `TextInput` itself is the root, so it takes the class
54
+ // directly. The panel/sheet forms wrap it in a home that becomes the root instead.
55
+ const isPill = surface !== 'panel' && surface !== 'sheet'
47
56
 
48
57
  const input = (
49
58
  <TextInput
@@ -55,14 +64,23 @@ export function SearchFilter({
55
64
  onChange={(event) => {
56
65
  draft.set(event.currentTarget.value)
57
66
  }}
67
+ {...(isPill && className !== undefined && { className })}
68
+ {...(isPill && style !== undefined && { style })}
58
69
  />
59
70
  )
60
71
 
61
- if (surface === 'sheet') {
72
+ // The sheet form is the panel form (`docs/CONTROLS-SPEC.md` §3: "sheet = panel rows inside a
73
+ // Drawer") — the same `PanelRow` label-above-control row on both surfaces.
74
+ if (surface === 'panel' || surface === 'sheet') {
62
75
  return (
63
- <SheetField label={label} labelId={labelId}>
76
+ <PanelRow
77
+ label={label}
78
+ labelId={labelId}
79
+ {...(className !== undefined && { className })}
80
+ {...(style !== undefined && { style })}
81
+ >
64
82
  {input}
65
- </SheetField>
83
+ </PanelRow>
66
84
  )
67
85
  }
68
86
  return input