basalt-ui 1.26.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 (1069) hide show
  1. package/AGENTS.md +23 -23
  2. package/CHANGELOG.md +67 -0
  3. package/MIGRATING.md +896 -37
  4. package/README.md +33 -3
  5. package/agent/rules/basalt-batteries.md +25 -10
  6. package/agent/rules/basalt-controls.md +73 -43
  7. package/agent/rules/basalt-mantine.md +17 -6
  8. package/agent/rules/basalt-state.md +5 -0
  9. package/agent/rules/basalt-tokens.md +1 -1
  10. package/configs/oxlint-plugin.js +1286 -73
  11. package/configs/oxlint-plugin.test.ts +1338 -29
  12. package/configs/oxlint.json +12 -5
  13. package/dist/agent/coalesce.d.ts.map +1 -1
  14. package/dist/agent/coalesce.js +2 -1
  15. package/dist/agent/merge.d.ts.map +1 -1
  16. package/dist/agent/merge.js +2 -1
  17. package/dist/agent/part-list.d.ts.map +1 -1
  18. package/dist/agent/part-list.js +2 -0
  19. package/dist/agent/stick-to-bottom.d.ts +3 -6
  20. package/dist/agent/stick-to-bottom.d.ts.map +1 -1
  21. package/dist/agent/stick-to-bottom.js +6 -3
  22. package/dist/agent-chat/composer.d.ts +6 -2
  23. package/dist/agent-chat/composer.d.ts.map +1 -1
  24. package/dist/agent-chat/composer.js +49 -33
  25. package/dist/agent-chat/relative-time.d.ts +0 -9
  26. package/dist/agent-chat/relative-time.d.ts.map +1 -1
  27. package/dist/agent-chat/relative-time.js +2 -1
  28. package/dist/agent-chat/thread-detail-panel.d.ts +3 -2
  29. package/dist/agent-chat/thread-detail-panel.d.ts.map +1 -1
  30. package/dist/agent-chat/thread-detail-panel.js +8 -5
  31. package/dist/agent-chat/thread-feed-row.d.ts +2 -1
  32. package/dist/agent-chat/thread-feed-row.d.ts.map +1 -1
  33. package/dist/agent-chat/thread-feed-row.js +92 -92
  34. package/dist/agent-chat/thread-feed.d.ts +3 -2
  35. package/dist/agent-chat/thread-feed.d.ts.map +1 -1
  36. package/dist/agent-chat/thread-feed.js +8 -3
  37. package/dist/agent-chat/thread-message.d.ts +4 -3
  38. package/dist/agent-chat/thread-message.d.ts.map +1 -1
  39. package/dist/agent-chat/thread-message.js +70 -31
  40. package/dist/agent-chat/thread-outcome-card.d.ts +3 -2
  41. package/dist/agent-chat/thread-outcome-card.d.ts.map +1 -1
  42. package/dist/agent-chat/thread-outcome-card.js +9 -2
  43. package/dist/agent-chat/thread-workspace.d.ts +7 -2
  44. package/dist/agent-chat/thread-workspace.d.ts.map +1 -1
  45. package/dist/agent-chat/thread-workspace.js +62 -29
  46. package/dist/agent-chat/tool-chip.d.ts +3 -2
  47. package/dist/agent-chat/tool-chip.d.ts.map +1 -1
  48. package/dist/agent-chat/tool-chip.js +7 -2
  49. package/dist/charts/hooks/useBandPlot.d.ts.map +1 -1
  50. package/dist/charts/hooks/useBandPlot.js +15 -8
  51. package/dist/charts/index.d.ts +8 -7
  52. package/dist/charts/index.d.ts.map +1 -1
  53. package/dist/charts/index.js +45 -5
  54. package/dist/charts/kinds/BandStrip.d.ts +10 -7
  55. package/dist/charts/kinds/BandStrip.d.ts.map +1 -1
  56. package/dist/charts/kinds/BandStrip.js +12 -7
  57. package/dist/charts/kinds/Bars.d.ts +12 -6
  58. package/dist/charts/kinds/Bars.d.ts.map +1 -1
  59. package/dist/charts/kinds/Bars.js +37 -11
  60. package/dist/charts/kinds/Donut.d.ts +6 -5
  61. package/dist/charts/kinds/Donut.d.ts.map +1 -1
  62. package/dist/charts/kinds/Donut.js +19 -2
  63. package/dist/charts/kinds/DualPanel.d.ts +6 -5
  64. package/dist/charts/kinds/DualPanel.d.ts.map +1 -1
  65. package/dist/charts/kinds/DualPanel.js +36 -8
  66. package/dist/charts/kinds/Heatmap.d.ts +10 -6
  67. package/dist/charts/kinds/Heatmap.d.ts.map +1 -1
  68. package/dist/charts/kinds/Heatmap.js +58 -31
  69. package/dist/charts/kinds/MirroredBars.d.ts +6 -5
  70. package/dist/charts/kinds/MirroredBars.d.ts.map +1 -1
  71. package/dist/charts/kinds/MirroredBars.js +9 -2
  72. package/dist/charts/kinds/MultiLine.d.ts +11 -6
  73. package/dist/charts/kinds/MultiLine.d.ts.map +1 -1
  74. package/dist/charts/kinds/MultiLine.js +24 -15
  75. package/dist/charts/kinds/StackedArea.d.ts +18 -8
  76. package/dist/charts/kinds/StackedArea.d.ts.map +1 -1
  77. package/dist/charts/kinds/StackedArea.js +29 -4
  78. package/dist/charts/kinds/ZonedLine.d.ts +12 -8
  79. package/dist/charts/kinds/ZonedLine.d.ts.map +1 -1
  80. package/dist/charts/kinds/ZonedLine.js +33 -23
  81. package/dist/charts/layout/auto-margin.d.ts +65 -8
  82. package/dist/charts/layout/auto-margin.d.ts.map +1 -1
  83. package/dist/charts/layout/auto-margin.js +45 -10
  84. package/dist/charts/layout/log-ticks.d.ts +27 -0
  85. package/dist/charts/layout/log-ticks.d.ts.map +1 -0
  86. package/dist/charts/layout/log-ticks.js +42 -0
  87. package/dist/charts/primitives/Axes.d.ts +11 -2
  88. package/dist/charts/primitives/Axes.d.ts.map +1 -1
  89. package/dist/charts/primitives/Axes.js +15 -5
  90. package/dist/charts/primitives/CartesianChart.d.ts +45 -7
  91. package/dist/charts/primitives/CartesianChart.d.ts.map +1 -1
  92. package/dist/charts/primitives/CartesianChart.js +86 -28
  93. package/dist/charts/primitives/ChartCard.d.ts +27 -2
  94. package/dist/charts/primitives/ChartCard.d.ts.map +1 -1
  95. package/dist/charts/primitives/ChartCard.js +37 -17
  96. package/dist/charts/primitives/ChartFrame.d.ts +30 -4
  97. package/dist/charts/primitives/ChartFrame.d.ts.map +1 -1
  98. package/dist/charts/primitives/ChartFrame.js +45 -15
  99. package/dist/charts/primitives/ChartLegend.d.ts +13 -2
  100. package/dist/charts/primitives/ChartLegend.d.ts.map +1 -1
  101. package/dist/charts/primitives/ChartLegend.js +33 -13
  102. package/dist/charts/primitives/ChartPending.d.ts +77 -4
  103. package/dist/charts/primitives/ChartPending.d.ts.map +1 -1
  104. package/dist/charts/primitives/ChartPending.js +100 -6
  105. package/dist/charts/primitives/ChartTooltip.d.ts +7 -0
  106. package/dist/charts/primitives/ChartTooltip.d.ts.map +1 -1
  107. package/dist/charts/primitives/ChartTooltip.js +18 -5
  108. package/dist/charts/primitives/Crosshair.d.ts +3 -0
  109. package/dist/charts/primitives/Crosshair.d.ts.map +1 -1
  110. package/dist/charts/primitives/Crosshair.js +3 -1
  111. package/dist/charts/primitives/chart-frame-layout.d.ts +95 -0
  112. package/dist/charts/primitives/chart-frame-layout.d.ts.map +1 -0
  113. package/dist/charts/primitives/chart-frame-layout.js +89 -0
  114. package/dist/charts/primitives/chart-tier.d.ts +16 -0
  115. package/dist/charts/primitives/chart-tier.d.ts.map +1 -0
  116. package/dist/charts/primitives/chart-tier.js +20 -0
  117. package/dist/charts/series.d.ts +55 -1
  118. package/dist/charts/series.d.ts.map +1 -1
  119. package/dist/charts/series.js +27 -1
  120. package/dist/charts/sparklines/BarSparkline.d.ts +3 -3
  121. package/dist/charts/sparklines/BarSparkline.d.ts.map +1 -1
  122. package/dist/charts/sparklines/BarSparkline.js +9 -3
  123. package/dist/charts/sparklines/LineSparkline.d.ts +3 -3
  124. package/dist/charts/sparklines/LineSparkline.d.ts.map +1 -1
  125. package/dist/charts/sparklines/LineSparkline.js +15 -3
  126. package/dist/charts/sparklines/index.d.ts +2 -2
  127. package/dist/charts/sparklines/index.d.ts.map +1 -1
  128. package/dist/charts/utils/format.d.ts +92 -2
  129. package/dist/charts/utils/format.d.ts.map +1 -1
  130. package/dist/charts/utils/format.js +64 -1
  131. package/dist/charts/utils/ticks.d.ts +81 -2
  132. package/dist/charts/utils/ticks.d.ts.map +1 -1
  133. package/dist/charts/utils/ticks.js +54 -4
  134. package/dist/cli/index.d.ts.map +1 -1
  135. package/dist/cli/index.js +2 -2
  136. package/dist/commands/ShortcutsHelp.d.ts +3 -2
  137. package/dist/commands/ShortcutsHelp.d.ts.map +1 -1
  138. package/dist/commands/ShortcutsHelp.js +8 -2
  139. package/dist/commands/define-commands.d.ts.map +1 -1
  140. package/dist/commands/define-commands.js +2 -2
  141. package/dist/commands/define-overlays.d.ts +102 -0
  142. package/dist/commands/define-overlays.d.ts.map +1 -1
  143. package/dist/commands/define-overlays.js +102 -2
  144. package/dist/commands/index.d.ts +10 -1
  145. package/dist/commands/index.d.ts.map +1 -1
  146. package/dist/commands/index.js +3 -1
  147. package/dist/commands/overlays-mount.d.ts +13 -6
  148. package/dist/commands/overlays-mount.d.ts.map +1 -1
  149. package/dist/commands/overlays-mount.js +23 -14
  150. package/dist/common/errors.d.ts +34 -0
  151. package/dist/common/errors.d.ts.map +1 -0
  152. package/dist/common/errors.js +36 -0
  153. package/dist/common/index.d.ts +15 -0
  154. package/dist/common/index.d.ts.map +1 -0
  155. package/dist/common/index.js +29 -0
  156. package/dist/common/props.d.ts +63 -0
  157. package/dist/common/props.d.ts.map +1 -0
  158. package/dist/common/props.js +6 -0
  159. package/dist/common/query-state-like.d.ts +43 -0
  160. package/dist/common/query-state-like.d.ts.map +1 -0
  161. package/dist/common/query-state-like.js +20 -0
  162. package/dist/common/refs.d.ts +29 -0
  163. package/dist/common/refs.d.ts.map +1 -0
  164. package/dist/common/refs.js +29 -0
  165. package/dist/common/scroll-parent.d.ts +23 -0
  166. package/dist/common/scroll-parent.d.ts.map +1 -0
  167. package/dist/common/scroll-parent.js +21 -0
  168. package/dist/common/validate.d.ts +38 -0
  169. package/dist/common/validate.d.ts.map +1 -0
  170. package/dist/common/validate.js +36 -0
  171. package/dist/connectivity/connectivity-indicator.d.ts +3 -1
  172. package/dist/connectivity/connectivity-indicator.d.ts.map +1 -1
  173. package/dist/connectivity/connectivity-indicator.js +3 -1
  174. package/dist/connectivity/connectivity-provider.d.ts.map +1 -1
  175. package/dist/connectivity/connectivity-provider.js +4 -1
  176. package/dist/connectivity/index.d.ts +1 -0
  177. package/dist/connectivity/index.d.ts.map +1 -1
  178. package/dist/connectivity/types.d.ts +2 -2
  179. package/dist/connectivity/types.d.ts.map +1 -1
  180. package/dist/connectivity/use-connectivity.d.ts.map +1 -1
  181. package/dist/content/article-layout.module.css +11 -4
  182. package/dist/content/callout.d.ts +8 -4
  183. package/dist/content/callout.d.ts.map +1 -1
  184. package/dist/content/callout.js +15 -8
  185. package/dist/content/guide.d.ts +5 -4
  186. package/dist/content/guide.d.ts.map +1 -1
  187. package/dist/content/guide.js +13 -4
  188. package/dist/content/heading-anchor.d.ts +24 -2
  189. package/dist/content/heading-anchor.d.ts.map +1 -1
  190. package/dist/content/heading-anchor.js +23 -9
  191. package/dist/content/index.d.ts.map +1 -1
  192. package/dist/content/markdown.d.ts.map +1 -1
  193. package/dist/content/markdown.js +2 -0
  194. package/dist/content/prose.module.css +9 -7
  195. package/dist/content/reading-progress.d.ts +10 -4
  196. package/dist/content/reading-progress.d.ts.map +1 -1
  197. package/dist/content/reading-progress.js +26 -10
  198. package/dist/content/slug.d.ts +4 -0
  199. package/dist/content/slug.d.ts.map +1 -1
  200. package/dist/content/toc.d.ts +11 -0
  201. package/dist/content/toc.d.ts.map +1 -1
  202. package/dist/content/toc.js +23 -10
  203. package/dist/controls/actions.d.ts +49 -6
  204. package/dist/controls/actions.d.ts.map +1 -1
  205. package/dist/controls/actions.js +39 -8
  206. package/dist/controls/compare-filter.d.ts +3 -2
  207. package/dist/controls/compare-filter.d.ts.map +1 -1
  208. package/dist/controls/compare-filter.js +17 -2
  209. package/dist/controls/control-group.d.ts +3 -3
  210. package/dist/controls/control-group.d.ts.map +1 -1
  211. package/dist/controls/control-group.js +9 -2
  212. package/dist/controls/controls.module.css +169 -186
  213. package/dist/controls/controls.module.css.d.ts +9 -11
  214. package/dist/controls/enum-filter.d.ts +13 -3
  215. package/dist/controls/enum-filter.d.ts.map +1 -1
  216. package/dist/controls/enum-filter.js +32 -15
  217. package/dist/controls/filter-context.d.ts +15 -6
  218. package/dist/controls/filter-context.d.ts.map +1 -1
  219. package/dist/controls/filter-pill.d.ts +3 -3
  220. package/dist/controls/filter-pill.d.ts.map +1 -1
  221. package/dist/controls/filter-pill.js +4 -1
  222. package/dist/controls/filter-set.d.ts +3 -2
  223. package/dist/controls/filter-set.d.ts.map +1 -1
  224. package/dist/controls/filter-set.js +66 -41
  225. package/dist/controls/filter-sheet.d.ts +20 -80
  226. package/dist/controls/filter-sheet.d.ts.map +1 -1
  227. package/dist/controls/filter-sheet.js +9 -95
  228. package/dist/controls/index.d.ts +3 -0
  229. package/dist/controls/index.d.ts.map +1 -1
  230. package/dist/controls/index.js +6 -0
  231. package/dist/controls/multi-select-filter.d.ts +18 -2
  232. package/dist/controls/multi-select-filter.d.ts.map +1 -1
  233. package/dist/controls/multi-select-filter.js +124 -22
  234. package/dist/controls/number-filter.d.ts +28 -0
  235. package/dist/controls/number-filter.d.ts.map +1 -0
  236. package/dist/controls/number-filter.js +172 -0
  237. package/dist/controls/panel-row.d.ts +132 -0
  238. package/dist/controls/panel-row.d.ts.map +1 -0
  239. package/dist/controls/panel-row.js +173 -0
  240. package/dist/controls/panel-row.module.css +86 -0
  241. package/dist/controls/panel-row.module.css.d.ts +13 -0
  242. package/dist/controls/range-filter.d.ts +20 -5
  243. package/dist/controls/range-filter.d.ts.map +1 -1
  244. package/dist/controls/range-filter.js +75 -38
  245. package/dist/controls/search-filter.d.ts +3 -2
  246. package/dist/controls/search-filter.d.ts.map +1 -1
  247. package/dist/controls/search-filter.js +26 -11
  248. package/dist/controls/select-filter.d.ts +2 -1
  249. package/dist/controls/select-filter.d.ts.map +1 -1
  250. package/dist/controls/select-filter.js +4 -0
  251. package/dist/controls/slider-control.d.ts +36 -0
  252. package/dist/controls/slider-control.d.ts.map +1 -0
  253. package/dist/controls/slider-control.js +64 -0
  254. package/dist/controls/sync-button.d.ts +3 -2
  255. package/dist/controls/sync-button.d.ts.map +1 -1
  256. package/dist/controls/sync-button.js +16 -3
  257. package/dist/controls/toggle-filter.d.ts +3 -2
  258. package/dist/controls/toggle-filter.d.ts.map +1 -1
  259. package/dist/controls/toggle-filter.js +29 -12
  260. package/dist/controls/view-tabs.d.ts +3 -2
  261. package/dist/controls/view-tabs.d.ts.map +1 -1
  262. package/dist/controls/view-tabs.js +43 -11
  263. package/dist/controls-dates/date-range-picker.d.ts +1 -1
  264. package/dist/controls-dates/date-range-picker.d.ts.map +1 -1
  265. package/dist/controls-dates/date-range-picker.js +6 -1
  266. package/dist/dashboard/delta-badge.d.ts +5 -1
  267. package/dist/dashboard/delta-badge.d.ts.map +1 -1
  268. package/dist/dashboard/empty-state.d.ts +10 -3
  269. package/dist/dashboard/empty-state.d.ts.map +1 -1
  270. package/dist/dashboard/empty-state.js +37 -6
  271. package/dist/dashboard/index.d.ts +6 -4
  272. package/dist/dashboard/index.d.ts.map +1 -1
  273. package/dist/dashboard/index.js +10 -2
  274. package/dist/dashboard/query-state.d.ts +48 -27
  275. package/dist/dashboard/query-state.d.ts.map +1 -1
  276. package/dist/dashboard/query-state.js +111 -57
  277. package/dist/dashboard/settings-section.d.ts +19 -9
  278. package/dist/dashboard/settings-section.d.ts.map +1 -1
  279. package/dist/dashboard/settings-section.js +74 -25
  280. package/dist/dashboard/stat-card.d.ts +76 -19
  281. package/dist/dashboard/stat-card.d.ts.map +1 -1
  282. package/dist/dashboard/stat-card.js +78 -38
  283. package/dist/dashboard/stat-card.module.css +112 -26
  284. package/dist/dashboard/stat-card.module.css.d.ts +5 -0
  285. package/dist/dashboard/stat-group.d.ts +41 -0
  286. package/dist/dashboard/stat-group.d.ts.map +1 -0
  287. package/dist/dashboard/stat-group.js +30 -0
  288. package/dist/dashboard/stat-group.module.css +66 -0
  289. package/dist/dashboard/stat-group.module.css.d.ts +8 -0
  290. package/dist/dashboard/widget-grid.d.ts +27 -0
  291. package/dist/dashboard/widget-grid.d.ts.map +1 -0
  292. package/dist/dashboard/widget-grid.js +41 -0
  293. package/dist/dashboard/widget-grid.module.css +53 -0
  294. package/dist/dashboard/widget-grid.module.css.d.ts +9 -0
  295. package/dist/data/data-table.d.ts +151 -12
  296. package/dist/data/data-table.d.ts.map +1 -1
  297. package/dist/data/data-table.js +392 -138
  298. package/dist/data/data-table.module.css +64 -0
  299. package/dist/data/data-table.module.css.d.ts +11 -0
  300. package/dist/data/index.d.ts +2 -2
  301. package/dist/data/index.d.ts.map +1 -1
  302. package/dist/data/index.js +24 -2
  303. package/dist/data/query-branch.d.ts +28 -0
  304. package/dist/data/query-branch.d.ts.map +1 -0
  305. package/dist/data/query-branch.js +10 -0
  306. package/dist/data/table.d.ts +2 -2
  307. package/dist/data/table.d.ts.map +1 -1
  308. package/dist/data/virtual-list.d.ts +65 -6
  309. package/dist/data/virtual-list.d.ts.map +1 -1
  310. package/dist/data/virtual-list.js +89 -38
  311. package/dist/data/virtual.d.ts +1 -1
  312. package/dist/data/virtual.d.ts.map +1 -1
  313. package/dist/forms/FormErrorSummary.d.ts +4 -3
  314. package/dist/forms/FormErrorSummary.d.ts.map +1 -1
  315. package/dist/forms/FormErrorSummary.js +6 -4
  316. package/dist/forms/create-form.d.ts +2 -2
  317. package/dist/forms/field.d.ts +46 -6
  318. package/dist/forms/field.d.ts.map +1 -1
  319. package/dist/forms/field.js +10 -5
  320. package/dist/forms/form-layout.d.ts +142 -0
  321. package/dist/forms/form-layout.d.ts.map +1 -0
  322. package/dist/forms/form-layout.js +110 -0
  323. package/dist/forms/form-layout.module.css +145 -0
  324. package/dist/forms/form-layout.module.css.d.ts +20 -0
  325. package/dist/forms/form-state.d.ts +42 -0
  326. package/dist/forms/form-state.d.ts.map +1 -0
  327. package/dist/forms/form-state.js +26 -0
  328. package/dist/forms/index.d.ts +22 -6
  329. package/dist/forms/index.d.ts.map +1 -1
  330. package/dist/forms/index.js +19 -3
  331. package/dist/forms/use-field-array.d.ts +60 -0
  332. package/dist/forms/use-field-array.d.ts.map +1 -0
  333. package/dist/forms/use-field-array.js +20 -0
  334. package/dist/forms/use-form-draft.d.ts +25 -10
  335. package/dist/forms/use-form-draft.d.ts.map +1 -1
  336. package/dist/forms/use-form-draft.js +55 -18
  337. package/dist/forms/use-form-submit.d.ts +88 -0
  338. package/dist/forms/use-form-submit.d.ts.map +1 -0
  339. package/dist/forms/use-form-submit.js +63 -0
  340. package/dist/guard/index.d.ts +1 -1
  341. package/dist/guard/index.d.ts.map +1 -1
  342. package/dist/guard/index.js +27 -11
  343. package/dist/index.d.ts +10 -8
  344. package/dist/index.d.ts.map +1 -1
  345. package/dist/index.js +44 -4
  346. package/dist/nav/types.d.ts +15 -6
  347. package/dist/nav/types.d.ts.map +1 -1
  348. package/dist/notifications/bell.d.ts +3 -2
  349. package/dist/notifications/bell.d.ts.map +1 -1
  350. package/dist/notifications/bell.js +73 -56
  351. package/dist/notifications/center.d.ts +15 -4
  352. package/dist/notifications/center.d.ts.map +1 -1
  353. package/dist/notifications/center.js +5 -2
  354. package/dist/notifications/define-notifications.d.ts +56 -14
  355. package/dist/notifications/define-notifications.d.ts.map +1 -1
  356. package/dist/notifications/define-notifications.js +2 -1
  357. package/dist/notifications/index.d.ts +6 -2
  358. package/dist/notifications/index.d.ts.map +1 -1
  359. package/dist/notifications/index.js +5 -1
  360. package/dist/notifications/mount-guard.d.ts +8 -0
  361. package/dist/notifications/mount-guard.d.ts.map +1 -0
  362. package/dist/notifications/mount-guard.js +19 -0
  363. package/dist/notifications/notify.d.ts +58 -1
  364. package/dist/notifications/notify.d.ts.map +1 -1
  365. package/dist/notifications/notify.js +46 -2
  366. package/dist/notifications/overlay.d.ts.map +1 -1
  367. package/dist/notifications/overlay.js +2 -0
  368. package/dist/notifications/store.d.ts +7 -0
  369. package/dist/notifications/store.d.ts.map +1 -1
  370. package/dist/provider/index.d.ts +37 -5
  371. package/dist/provider/index.d.ts.map +1 -1
  372. package/dist/provider/index.js +66 -28
  373. package/dist/provider/lab-theme.d.ts.map +1 -1
  374. package/dist/provider/lab-theme.js +2 -1
  375. package/dist/query/devtools.d.ts.map +1 -1
  376. package/dist/query/devtools.js +1 -0
  377. package/dist/router-tanstack/field.d.ts +1 -1
  378. package/dist/router-tanstack/field.d.ts.map +1 -1
  379. package/dist/router-tanstack/index.d.ts +1 -1
  380. package/dist/router-tanstack/index.d.ts.map +1 -1
  381. package/dist/router-tanstack/search-store.d.ts +4 -1
  382. package/dist/router-tanstack/search-store.d.ts.map +1 -1
  383. package/dist/router-tanstack/search-store.js +92 -8
  384. package/dist/router-tanstack/use-nav.d.ts.map +1 -1
  385. package/dist/router-tanstack/use-nav.js +1 -1
  386. package/dist/section/index.d.ts +1 -1
  387. package/dist/section/index.d.ts.map +1 -1
  388. package/dist/section/section.d.ts +36 -2
  389. package/dist/section/section.d.ts.map +1 -1
  390. package/dist/section/section.js +49 -57
  391. package/dist/section/section.module.css +11 -0
  392. package/dist/shell/account-types.d.ts +5 -2
  393. package/dist/shell/account-types.d.ts.map +1 -1
  394. package/dist/shell/app-brand.d.ts +13 -0
  395. package/dist/shell/app-brand.d.ts.map +1 -0
  396. package/dist/shell/app-brand.js +94 -0
  397. package/dist/shell/app-brand.module.css +109 -0
  398. package/dist/shell/app-brand.module.css.d.ts +14 -0
  399. package/dist/shell/app-breadcrumbs.d.ts +4 -2
  400. package/dist/shell/app-breadcrumbs.d.ts.map +1 -1
  401. package/dist/shell/app-breadcrumbs.js +33 -22
  402. package/dist/shell/app-header.module.css +10 -5
  403. package/dist/shell/app-main.module.css +107 -0
  404. package/dist/shell/app-main.module.css.d.ts +11 -0
  405. package/dist/shell/app-mobile-nav.d.ts +3 -2
  406. package/dist/shell/app-mobile-nav.d.ts.map +1 -1
  407. package/dist/shell/app-mobile-nav.js +29 -15
  408. package/dist/shell/app-mobile-nav.module.css +48 -27
  409. package/dist/shell/app-mobile-nav.module.css.d.ts +0 -1
  410. package/dist/shell/app-sidebar-account.d.ts +2 -1
  411. package/dist/shell/app-sidebar-account.d.ts.map +1 -1
  412. package/dist/shell/app-sidebar-account.js +57 -22
  413. package/dist/shell/app-sidebar.d.ts +9 -3
  414. package/dist/shell/app-sidebar.d.ts.map +1 -1
  415. package/dist/shell/app-sidebar.js +55 -109
  416. package/dist/shell/app-sidebar.module.css +53 -96
  417. package/dist/shell/app-sidebar.module.css.d.ts +0 -5
  418. package/dist/shell/index.d.ts +14 -6
  419. package/dist/shell/index.d.ts.map +1 -1
  420. package/dist/shell/index.js +47 -12
  421. package/dist/shell/isomorphic-layout-effect.d.ts +14 -0
  422. package/dist/shell/isomorphic-layout-effect.d.ts.map +1 -0
  423. package/dist/shell/isomorphic-layout-effect.js +5 -0
  424. package/dist/shell/mobile-nav-model.d.ts.map +1 -1
  425. package/dist/shell/mobile-nav-model.js +27 -6
  426. package/dist/shell/nav-count-badge.d.ts +5 -3
  427. package/dist/shell/nav-count-badge.d.ts.map +1 -1
  428. package/dist/shell/nav-count-badge.js +3 -1
  429. package/dist/shell/page-aside.d.ts +43 -0
  430. package/dist/shell/page-aside.d.ts.map +1 -0
  431. package/dist/shell/page-aside.js +202 -0
  432. package/dist/shell/page-aside.module.css +174 -0
  433. package/dist/shell/page-aside.module.css.d.ts +14 -0
  434. package/dist/shell/page-bar.d.ts +55 -15
  435. package/dist/shell/page-bar.d.ts.map +1 -1
  436. package/dist/shell/page-bar.js +130 -41
  437. package/dist/shell/page-bar.module.css +107 -13
  438. package/dist/shell/page-bar.module.css.d.ts +4 -1
  439. package/dist/shell/sidebar-search.d.ts +4 -2
  440. package/dist/shell/sidebar-search.d.ts.map +1 -1
  441. package/dist/shell/sidebar-search.js +9 -4
  442. package/dist/shell/sidebar-search.module.css +1 -1
  443. package/dist/state/fields.d.ts +227 -34
  444. package/dist/state/fields.d.ts.map +1 -1
  445. package/dist/state/fields.js +77 -30
  446. package/dist/state/persisted-or-local.d.ts +9 -0
  447. package/dist/state/persisted-or-local.d.ts.map +1 -0
  448. package/dist/state/persisted-or-local.js +24 -0
  449. package/dist/state/persisted.d.ts +12 -0
  450. package/dist/state/persisted.d.ts.map +1 -1
  451. package/dist/state/persisted.js +36 -18
  452. package/dist/state.d.ts +1 -1
  453. package/dist/state.d.ts.map +1 -1
  454. package/dist/state.js +2 -6
  455. package/dist/surfaces.d.ts +12 -11
  456. package/dist/surfaces.d.ts.map +1 -1
  457. package/dist/surfaces.js +43 -12
  458. package/dist/theme/ctl-theme.d.ts.map +1 -1
  459. package/dist/theme/index.d.ts.map +1 -1
  460. package/dist/theme/index.js +102 -17
  461. package/dist/theme/nav-link.module.css +37 -26
  462. package/dist/theme/segmented-control.module.css +22 -0
  463. package/dist/theme/segmented-control.module.css.d.ts +1 -0
  464. package/dist/theme-lab/boot.d.ts +66 -0
  465. package/dist/theme-lab/boot.d.ts.map +1 -0
  466. package/dist/theme-lab/boot.js +82 -0
  467. package/dist/theme-lab/index.d.ts +4 -43
  468. package/dist/theme-lab/index.d.ts.map +1 -1
  469. package/dist/theme-lab/index.js +9 -69
  470. package/dist/theme-toggle/index.d.ts +3 -2
  471. package/dist/theme-toggle/index.d.ts.map +1 -1
  472. package/dist/theme-toggle/index.js +8 -2
  473. package/dist/tokens/index.d.ts +42 -0
  474. package/dist/tokens/index.d.ts.map +1 -1
  475. package/dist/tokens/index.js +35 -6
  476. package/dist/tokens/palette.d.ts +45 -25
  477. package/dist/tokens/palette.d.ts.map +1 -1
  478. package/dist/tokens/palette.js +31 -14
  479. package/dist/tokens.css +4 -5
  480. package/dist/utils/is-dev.d.ts +3 -0
  481. package/dist/utils/is-dev.d.ts.map +1 -0
  482. package/dist/utils/is-dev.js +6 -0
  483. package/dist/widget-header/delta-badge.d.ts +7 -1
  484. package/dist/widget-header/delta-badge.d.ts.map +1 -1
  485. package/dist/widget-header/delta-badge.js +8 -1
  486. package/dist/widget-header/index.d.ts +2 -2
  487. package/dist/widget-header/index.d.ts.map +1 -1
  488. package/dist/widget-header/index.js +3 -1
  489. package/dist/widget-header/widget-header.d.ts +99 -4
  490. package/dist/widget-header/widget-header.d.ts.map +1 -1
  491. package/dist/widget-header/widget-header.js +45 -18
  492. package/dist/widget-header/widget-header.module.css +87 -7
  493. package/dist/widget-header/widget-header.module.css.d.ts +1 -0
  494. package/llms.txt +6 -6
  495. package/package.json +49 -25
  496. package/src/agent/coalesce.ts +2 -1
  497. package/src/agent/merge.ts +2 -1
  498. package/src/agent/part-list.tsx +2 -0
  499. package/src/agent/stick-to-bottom.tsx +9 -11
  500. package/src/agent-chat/composer.tsx +124 -103
  501. package/src/agent-chat/relative-time.ts +2 -1
  502. package/src/agent-chat/thread-detail-panel.tsx +9 -5
  503. package/src/agent-chat/thread-feed-row.tsx +17 -9
  504. package/src/agent-chat/thread-feed.tsx +9 -3
  505. package/src/agent-chat/thread-message.tsx +48 -13
  506. package/src/agent-chat/thread-outcome-card.tsx +9 -1
  507. package/src/agent-chat/thread-workspace.tsx +72 -33
  508. package/src/agent-chat/tool-chip.tsx +8 -2
  509. package/src/charts/hooks/useBandPlot.ts +26 -8
  510. package/src/charts/index.ts +39 -4
  511. package/src/charts/kinds/BandStrip.tsx +36 -14
  512. package/src/charts/kinds/Bars.tsx +67 -16
  513. package/src/charts/kinds/Donut.tsx +30 -3
  514. package/src/charts/kinds/DualPanel.tsx +56 -10
  515. package/src/charts/kinds/Heatmap.tsx +90 -33
  516. package/src/charts/kinds/MirroredBars.tsx +21 -3
  517. package/src/charts/kinds/MultiLine.tsx +61 -18
  518. package/src/charts/kinds/StackedArea.tsx +77 -6
  519. package/src/charts/kinds/ZonedLine.tsx +71 -31
  520. package/src/charts/layout/auto-margin.ts +134 -21
  521. package/src/charts/layout/log-ticks.ts +90 -0
  522. package/src/charts/primitives/Axes.tsx +33 -5
  523. package/src/charts/primitives/CartesianChart.tsx +194 -41
  524. package/src/charts/primitives/ChartCard.tsx +66 -25
  525. package/src/charts/primitives/ChartFrame.tsx +107 -26
  526. package/src/charts/primitives/ChartLegend.tsx +43 -13
  527. package/src/charts/primitives/ChartPending.tsx +185 -7
  528. package/src/charts/primitives/ChartTooltip.tsx +37 -5
  529. package/src/charts/primitives/Crosshair.tsx +6 -3
  530. package/src/charts/primitives/chart-frame-layout.ts +212 -0
  531. package/src/charts/primitives/chart-tier.tsx +37 -0
  532. package/src/charts/series.ts +77 -1
  533. package/src/charts/sparklines/BarSparkline.tsx +11 -3
  534. package/src/charts/sparklines/LineSparkline.tsx +18 -4
  535. package/src/charts/sparklines/index.ts +2 -2
  536. package/src/charts/utils/format.ts +163 -2
  537. package/src/charts/utils/ticks.ts +149 -4
  538. package/src/cli/index.ts +8 -3
  539. package/src/commands/ShortcutsHelp.tsx +10 -3
  540. package/src/commands/define-commands.ts +2 -2
  541. package/src/commands/define-overlays.ts +216 -1
  542. package/src/commands/index.ts +12 -0
  543. package/src/commands/overlays-mount.tsx +60 -26
  544. package/src/common/errors.ts +73 -0
  545. package/src/common/index.ts +21 -0
  546. package/src/common/props.ts +71 -0
  547. package/src/common/query-state-like.ts +64 -0
  548. package/src/common/refs.ts +58 -0
  549. package/src/common/scroll-parent.ts +42 -0
  550. package/src/common/validate.ts +94 -0
  551. package/src/connectivity/connectivity-indicator.tsx +6 -1
  552. package/src/connectivity/connectivity-provider.tsx +4 -1
  553. package/src/connectivity/index.ts +1 -0
  554. package/src/connectivity/types.ts +5 -2
  555. package/src/connectivity/use-connectivity.ts +3 -2
  556. package/src/content/article-layout.module.css +11 -4
  557. package/src/content/callout.tsx +13 -7
  558. package/src/content/guide.tsx +14 -4
  559. package/src/content/heading-anchor.tsx +14 -3
  560. package/src/content/index.ts +2 -0
  561. package/src/content/markdown.tsx +2 -0
  562. package/src/content/prose.module.css +9 -7
  563. package/src/content/reading-progress.tsx +40 -15
  564. package/src/content/slug.ts +4 -0
  565. package/src/content/toc.tsx +47 -10
  566. package/src/controls/actions.tsx +89 -9
  567. package/src/controls/compare-filter.tsx +20 -3
  568. package/src/controls/control-group.tsx +11 -4
  569. package/src/controls/controls.module.css +169 -186
  570. package/src/controls/controls.module.css.d.ts +9 -11
  571. package/src/controls/enum-filter.tsx +46 -19
  572. package/src/controls/filter-context.tsx +21 -8
  573. package/src/controls/filter-pill.tsx +6 -3
  574. package/src/controls/filter-set.tsx +32 -13
  575. package/src/controls/filter-sheet.tsx +44 -194
  576. package/src/controls/index.ts +8 -0
  577. package/src/controls/multi-select-filter.tsx +179 -29
  578. package/src/controls/number-filter.tsx +329 -0
  579. package/src/controls/panel-row.module.css +86 -0
  580. package/src/controls/panel-row.module.css.d.ts +13 -0
  581. package/src/controls/panel-row.tsx +392 -0
  582. package/src/controls/range-filter.tsx +133 -43
  583. package/src/controls/search-filter.tsx +31 -13
  584. package/src/controls/select-filter.tsx +11 -1
  585. package/src/controls/slider-control.tsx +159 -0
  586. package/src/controls/sync-button.tsx +13 -3
  587. package/src/controls/toggle-filter.tsx +37 -17
  588. package/src/controls/view-tabs.tsx +63 -11
  589. package/src/controls-dates/date-range-picker.tsx +8 -1
  590. package/src/dashboard/delta-badge.tsx +5 -1
  591. package/src/dashboard/empty-state.tsx +72 -25
  592. package/src/dashboard/index.ts +19 -2
  593. package/src/dashboard/query-state.tsx +154 -84
  594. package/src/dashboard/settings-section.tsx +87 -31
  595. package/src/dashboard/stat-card.module.css +112 -26
  596. package/src/dashboard/stat-card.module.css.d.ts +5 -0
  597. package/src/dashboard/stat-card.tsx +207 -68
  598. package/src/dashboard/stat-group.module.css +66 -0
  599. package/src/dashboard/stat-group.module.css.d.ts +8 -0
  600. package/src/dashboard/stat-group.tsx +74 -0
  601. package/src/dashboard/widget-grid.module.css +53 -0
  602. package/src/dashboard/widget-grid.module.css.d.ts +9 -0
  603. package/src/dashboard/widget-grid.tsx +127 -0
  604. package/src/data/data-table.module.css +64 -0
  605. package/src/data/data-table.module.css.d.ts +11 -0
  606. package/src/data/data-table.tsx +927 -346
  607. package/src/data/index.ts +30 -2
  608. package/src/data/query-branch.ts +53 -0
  609. package/src/data/table.ts +5 -0
  610. package/src/data/virtual-list.tsx +189 -55
  611. package/src/data/virtual.ts +5 -1
  612. package/src/forms/FormErrorSummary.tsx +13 -6
  613. package/src/forms/create-form.ts +2 -2
  614. package/src/forms/field.ts +60 -11
  615. package/src/forms/form-layout.module.css +145 -0
  616. package/src/forms/form-layout.module.css.d.ts +20 -0
  617. package/src/forms/form-layout.tsx +305 -0
  618. package/src/forms/form-state.tsx +94 -0
  619. package/src/forms/index.ts +38 -7
  620. package/src/forms/use-field-array.ts +94 -0
  621. package/src/forms/use-form-draft.ts +145 -53
  622. package/src/forms/use-form-submit.ts +199 -0
  623. package/src/guard/index.ts +91 -23
  624. package/src/index.ts +79 -10
  625. package/src/nav/types.ts +15 -6
  626. package/src/notifications/bell.tsx +18 -4
  627. package/src/notifications/center.tsx +18 -5
  628. package/src/notifications/define-notifications.ts +64 -18
  629. package/src/notifications/index.ts +12 -1
  630. package/src/notifications/mount-guard.ts +41 -0
  631. package/src/notifications/notify.tsx +122 -3
  632. package/src/notifications/overlay.tsx +2 -0
  633. package/src/notifications/store.ts +7 -0
  634. package/src/provider/index.tsx +101 -13
  635. package/src/provider/lab-theme.ts +2 -1
  636. package/src/query/devtools.tsx +1 -0
  637. package/src/router-tanstack/field.ts +5 -0
  638. package/src/router-tanstack/index.ts +4 -0
  639. package/src/router-tanstack/search-store.ts +171 -12
  640. package/src/router-tanstack/use-nav.ts +18 -13
  641. package/src/section/index.ts +1 -1
  642. package/src/section/section.module.css +11 -0
  643. package/src/section/section.tsx +163 -96
  644. package/src/shell/account-types.ts +5 -2
  645. package/src/shell/app-brand.module.css +109 -0
  646. package/src/shell/app-brand.module.css.d.ts +14 -0
  647. package/src/shell/app-brand.tsx +140 -0
  648. package/src/shell/app-breadcrumbs.tsx +19 -9
  649. package/src/shell/app-header.module.css +10 -5
  650. package/src/shell/app-main.module.css +107 -0
  651. package/src/shell/app-main.module.css.d.ts +11 -0
  652. package/src/shell/app-mobile-nav.module.css +48 -27
  653. package/src/shell/app-mobile-nav.module.css.d.ts +0 -1
  654. package/src/shell/app-mobile-nav.tsx +43 -11
  655. package/src/shell/app-sidebar-account.tsx +29 -6
  656. package/src/shell/app-sidebar.module.css +53 -96
  657. package/src/shell/app-sidebar.module.css.d.ts +0 -5
  658. package/src/shell/app-sidebar.tsx +40 -113
  659. package/src/shell/index.tsx +156 -65
  660. package/src/shell/isomorphic-layout-effect.ts +14 -0
  661. package/src/shell/mobile-nav-model.ts +66 -10
  662. package/src/shell/nav-count-badge.tsx +6 -1
  663. package/src/shell/page-aside.module.css +174 -0
  664. package/src/shell/page-aside.module.css.d.ts +14 -0
  665. package/src/shell/page-aside.tsx +377 -0
  666. package/src/shell/page-bar.module.css +107 -13
  667. package/src/shell/page-bar.module.css.d.ts +4 -1
  668. package/src/shell/page-bar.tsx +294 -94
  669. package/src/shell/sidebar-search.module.css +1 -1
  670. package/src/shell/sidebar-search.tsx +13 -4
  671. package/src/state/fields.ts +360 -69
  672. package/src/state/persisted-or-local.ts +50 -0
  673. package/src/state/persisted.ts +76 -26
  674. package/src/state.ts +4 -7
  675. package/src/surfaces.ts +43 -12
  676. package/src/theme/ctl-theme.tsx +10 -0
  677. package/src/theme/index.ts +129 -16
  678. package/src/theme/nav-link.module.css +37 -26
  679. package/src/theme/segmented-control.module.css +22 -0
  680. package/src/theme/segmented-control.module.css.d.ts +1 -0
  681. package/src/theme-lab/boot.ts +140 -0
  682. package/src/theme-lab/index.tsx +21 -112
  683. package/src/theme-toggle/index.tsx +10 -3
  684. package/src/tokens/index.ts +50 -6
  685. package/src/tokens/palette.ts +58 -30
  686. package/src/utils/is-dev.ts +4 -0
  687. package/src/widget-header/delta-badge.tsx +25 -5
  688. package/src/widget-header/index.ts +10 -2
  689. package/src/widget-header/widget-header.module.css +87 -7
  690. package/src/widget-header/widget-header.module.css.d.ts +1 -0
  691. package/src/widget-header/widget-header.tsx +166 -42
  692. package/dist/agent/adapter.test.d.ts +0 -2
  693. package/dist/agent/adapter.test.d.ts.map +0 -1
  694. package/dist/agent/ai-sdk-transport.test.d.ts +0 -2
  695. package/dist/agent/ai-sdk-transport.test.d.ts.map +0 -1
  696. package/dist/agent/coalesce.test.d.ts +0 -2
  697. package/dist/agent/coalesce.test.d.ts.map +0 -1
  698. package/dist/agent/foreign.test.d.ts +0 -2
  699. package/dist/agent/foreign.test.d.ts.map +0 -1
  700. package/dist/agent/id.test.d.ts +0 -2
  701. package/dist/agent/id.test.d.ts.map +0 -1
  702. package/dist/agent/merge.test.d.ts +0 -2
  703. package/dist/agent/merge.test.d.ts.map +0 -1
  704. package/dist/agent/part-list.test.d.ts +0 -2
  705. package/dist/agent/part-list.test.d.ts.map +0 -1
  706. package/dist/agent/parts.test.d.ts +0 -2
  707. package/dist/agent/parts.test.d.ts.map +0 -1
  708. package/dist/agent/thread.test.d.ts +0 -2
  709. package/dist/agent/thread.test.d.ts.map +0 -1
  710. package/dist/agent/transport.test.d.ts +0 -2
  711. package/dist/agent/transport.test.d.ts.map +0 -1
  712. package/dist/agent/use-agent-stream.test.d.ts +0 -2
  713. package/dist/agent/use-agent-stream.test.d.ts.map +0 -1
  714. package/dist/agent/use-agent-thread-runs.foreign.test.d.ts +0 -2
  715. package/dist/agent/use-agent-thread-runs.foreign.test.d.ts.map +0 -1
  716. package/dist/agent/use-agent-thread-runs.resume.test.d.ts +0 -2
  717. package/dist/agent/use-agent-thread-runs.resume.test.d.ts.map +0 -1
  718. package/dist/agent/use-agent-thread-runs.test.d.ts +0 -2
  719. package/dist/agent/use-agent-thread-runs.test.d.ts.map +0 -1
  720. package/dist/agent/use-agent-thread-runs.wedge.test.d.ts +0 -2
  721. package/dist/agent/use-agent-thread-runs.wedge.test.d.ts.map +0 -1
  722. package/dist/agent-chat/composer.test.d.ts +0 -2
  723. package/dist/agent-chat/composer.test.d.ts.map +0 -1
  724. package/dist/agent-chat/relative-time.test.d.ts +0 -2
  725. package/dist/agent-chat/relative-time.test.d.ts.map +0 -1
  726. package/dist/agent-chat/thread-feed-row.test.d.ts +0 -2
  727. package/dist/agent-chat/thread-feed-row.test.d.ts.map +0 -1
  728. package/dist/agent-chat/thread-feed.test.d.ts +0 -2
  729. package/dist/agent-chat/thread-feed.test.d.ts.map +0 -1
  730. package/dist/agent-chat/thread-message.test.d.ts +0 -2
  731. package/dist/agent-chat/thread-message.test.d.ts.map +0 -1
  732. package/dist/agent-chat/thread-workspace.test.d.ts +0 -2
  733. package/dist/agent-chat/thread-workspace.test.d.ts.map +0 -1
  734. package/dist/agent-chat/tool-chip.test.d.ts +0 -2
  735. package/dist/agent-chat/tool-chip.test.d.ts.map +0 -1
  736. package/dist/agent-chat/virtualize.type-guard.test.d.ts +0 -2
  737. package/dist/agent-chat/virtualize.type-guard.test.d.ts.map +0 -1
  738. package/dist/charts/cursor/resolve.test.d.ts +0 -2
  739. package/dist/charts/cursor/resolve.test.d.ts.map +0 -1
  740. package/dist/charts/cursor/store.test.d.ts +0 -2
  741. package/dist/charts/cursor/store.test.d.ts.map +0 -1
  742. package/dist/charts/kinds/BandStrip.test.d.ts +0 -2
  743. package/dist/charts/kinds/BandStrip.test.d.ts.map +0 -1
  744. package/dist/charts/kinds/Bars.test.d.ts +0 -2
  745. package/dist/charts/kinds/Bars.test.d.ts.map +0 -1
  746. package/dist/charts/kinds/DualPanel.test.d.ts +0 -2
  747. package/dist/charts/kinds/DualPanel.test.d.ts.map +0 -1
  748. package/dist/charts/kinds/MirroredBars.test.d.ts +0 -2
  749. package/dist/charts/kinds/MirroredBars.test.d.ts.map +0 -1
  750. package/dist/charts/kinds/MultiLine.test.d.ts +0 -2
  751. package/dist/charts/kinds/MultiLine.test.d.ts.map +0 -1
  752. package/dist/charts/kinds/StackedArea.test.d.ts +0 -2
  753. package/dist/charts/kinds/StackedArea.test.d.ts.map +0 -1
  754. package/dist/charts/kinds/ZonedLine.test.d.ts +0 -2
  755. package/dist/charts/kinds/ZonedLine.test.d.ts.map +0 -1
  756. package/dist/charts/layout/auto-margin.test.d.ts +0 -2
  757. package/dist/charts/layout/auto-margin.test.d.ts.map +0 -1
  758. package/dist/charts/primitives/CartesianChart.test.d.ts +0 -2
  759. package/dist/charts/primitives/CartesianChart.test.d.ts.map +0 -1
  760. package/dist/charts/primitives/ChartCard.test.d.ts +0 -2
  761. package/dist/charts/primitives/ChartCard.test.d.ts.map +0 -1
  762. package/dist/charts/primitives/ChartFrame.test.d.ts +0 -2
  763. package/dist/charts/primitives/ChartFrame.test.d.ts.map +0 -1
  764. package/dist/charts/primitives/ChartLegend.test.d.ts +0 -2
  765. package/dist/charts/primitives/ChartLegend.test.d.ts.map +0 -1
  766. package/dist/charts/primitives/ChartTooltip.test.d.ts +0 -2
  767. package/dist/charts/primitives/ChartTooltip.test.d.ts.map +0 -1
  768. package/dist/charts/primitives/XZoneRects.test.d.ts +0 -2
  769. package/dist/charts/primitives/XZoneRects.test.d.ts.map +0 -1
  770. package/dist/charts/series.test.d.ts +0 -2
  771. package/dist/charts/series.test.d.ts.map +0 -1
  772. package/dist/charts/utils/domain.test.d.ts +0 -2
  773. package/dist/charts/utils/domain.test.d.ts.map +0 -1
  774. package/dist/charts/utils/measure-text.test.d.ts +0 -2
  775. package/dist/charts/utils/measure-text.test.d.ts.map +0 -1
  776. package/dist/cli/check-coverage.test.d.ts +0 -2
  777. package/dist/cli/check-coverage.test.d.ts.map +0 -1
  778. package/dist/cli/check-theme.test.d.ts +0 -2
  779. package/dist/cli/check-theme.test.d.ts.map +0 -1
  780. package/dist/cli/help.test.d.ts +0 -2
  781. package/dist/cli/help.test.d.ts.map +0 -1
  782. package/dist/cli/init-sync.test.d.ts +0 -2
  783. package/dist/cli/init-sync.test.d.ts.map +0 -1
  784. package/dist/cli/placement-engine.test.d.ts +0 -2
  785. package/dist/cli/placement-engine.test.d.ts.map +0 -1
  786. package/dist/cli/shipped-versions.test.d.ts +0 -2
  787. package/dist/cli/shipped-versions.test.d.ts.map +0 -1
  788. package/dist/cli/tarball-parity.test.d.ts +0 -2
  789. package/dist/cli/tarball-parity.test.d.ts.map +0 -1
  790. package/dist/cli/template-format.test.d.ts +0 -2
  791. package/dist/cli/template-format.test.d.ts.map +0 -1
  792. package/dist/cli/template-ledger.test.d.ts +0 -2
  793. package/dist/cli/template-ledger.test.d.ts.map +0 -1
  794. package/dist/cli/tokens-css.test.d.ts +0 -2
  795. package/dist/cli/tokens-css.test.d.ts.map +0 -1
  796. package/dist/cli/toolchain-wiring.test.d.ts +0 -2
  797. package/dist/cli/toolchain-wiring.test.d.ts.map +0 -1
  798. package/dist/cli/waiver-audit.test.d.ts +0 -2
  799. package/dist/cli/waiver-audit.test.d.ts.map +0 -1
  800. package/dist/content/article-actions.test.d.ts +0 -2
  801. package/dist/content/article-actions.test.d.ts.map +0 -1
  802. package/dist/content/article-model.test.d.ts +0 -2
  803. package/dist/content/article-model.test.d.ts.map +0 -1
  804. package/dist/content/block-split.test.d.ts +0 -2
  805. package/dist/content/block-split.test.d.ts.map +0 -1
  806. package/dist/content/heading-components.test.d.ts +0 -2
  807. package/dist/content/heading-components.test.d.ts.map +0 -1
  808. package/dist/content/markdown.test.d.ts +0 -2
  809. package/dist/content/markdown.test.d.ts.map +0 -1
  810. package/dist/content/prose.module.css.test.d.ts +0 -2
  811. package/dist/content/prose.module.css.test.d.ts.map +0 -1
  812. package/dist/content/sanitize.test.d.ts +0 -2
  813. package/dist/content/sanitize.test.d.ts.map +0 -1
  814. package/dist/content/slug.test.d.ts +0 -2
  815. package/dist/content/slug.test.d.ts.map +0 -1
  816. package/dist/controls/actions.test.d.ts +0 -2
  817. package/dist/controls/actions.test.d.ts.map +0 -1
  818. package/dist/controls/actions.type-guard.test.d.ts +0 -2
  819. package/dist/controls/actions.type-guard.test.d.ts.map +0 -1
  820. package/dist/controls/control-group.test.d.ts +0 -2
  821. package/dist/controls/control-group.test.d.ts.map +0 -1
  822. package/dist/controls/controls.router.test.d.ts +0 -2
  823. package/dist/controls/controls.router.test.d.ts.map +0 -1
  824. package/dist/controls/filter-set.test.d.ts +0 -2
  825. package/dist/controls/filter-set.test.d.ts.map +0 -1
  826. package/dist/controls/peer-boundary.test.d.ts +0 -2
  827. package/dist/controls/peer-boundary.test.d.ts.map +0 -1
  828. package/dist/controls/props-contract.test.d.ts +0 -2
  829. package/dist/controls/props-contract.test.d.ts.map +0 -1
  830. package/dist/controls/sync-button.test.d.ts +0 -2
  831. package/dist/controls/sync-button.test.d.ts.map +0 -1
  832. package/dist/dashboard/query-state.test.d.ts +0 -2
  833. package/dist/dashboard/query-state.test.d.ts.map +0 -1
  834. package/dist/dashboard/settings-section.test.d.ts +0 -2
  835. package/dist/dashboard/settings-section.test.d.ts.map +0 -1
  836. package/dist/dashboard/stat-card.test.d.ts +0 -2
  837. package/dist/dashboard/stat-card.test.d.ts.map +0 -1
  838. package/dist/data/data-table.test.d.ts +0 -2
  839. package/dist/data/data-table.test.d.ts.map +0 -1
  840. package/dist/guard/check-source.test.d.ts +0 -2
  841. package/dist/guard/check-source.test.d.ts.map +0 -1
  842. package/dist/guard/grace.test.d.ts +0 -20
  843. package/dist/guard/grace.test.d.ts.map +0 -1
  844. package/dist/guard/guard-hook.test.d.ts +0 -2
  845. package/dist/guard/guard-hook.test.d.ts.map +0 -1
  846. package/dist/provider/index.test.d.ts +0 -2
  847. package/dist/provider/index.test.d.ts.map +0 -1
  848. package/dist/provider/lab-theme.test.d.ts +0 -2
  849. package/dist/provider/lab-theme.test.d.ts.map +0 -1
  850. package/dist/query/error-message.test.d.ts +0 -2
  851. package/dist/query/error-message.test.d.ts.map +0 -1
  852. package/dist/router-tanstack/multi-search-param-store.test.d.ts +0 -2
  853. package/dist/router-tanstack/multi-search-param-store.test.d.ts.map +0 -1
  854. package/dist/router-tanstack/nav.test.d.ts +0 -2
  855. package/dist/router-tanstack/nav.test.d.ts.map +0 -1
  856. package/dist/router-tanstack/search-param-store.test.d.ts +0 -2
  857. package/dist/router-tanstack/search-param-store.test.d.ts.map +0 -1
  858. package/dist/router-tanstack/search-store.router.test.d.ts +0 -2
  859. package/dist/router-tanstack/search-store.router.test.d.ts.map +0 -1
  860. package/dist/router-tanstack/search-store.test.d.ts +0 -2
  861. package/dist/router-tanstack/search-store.test.d.ts.map +0 -1
  862. package/dist/router-tanstack/use-nav.test.d.ts +0 -2
  863. package/dist/router-tanstack/use-nav.test.d.ts.map +0 -1
  864. package/dist/section/section.test.d.ts +0 -2
  865. package/dist/section/section.test.d.ts.map +0 -1
  866. package/dist/shell/app-mobile-nav.test.d.ts +0 -2
  867. package/dist/shell/app-mobile-nav.test.d.ts.map +0 -1
  868. package/dist/shell/app-sidebar.test.d.ts +0 -2
  869. package/dist/shell/app-sidebar.test.d.ts.map +0 -1
  870. package/dist/shell/index.test.d.ts +0 -2
  871. package/dist/shell/index.test.d.ts.map +0 -1
  872. package/dist/shell/mobile-nav-model.test.d.ts +0 -2
  873. package/dist/shell/mobile-nav-model.test.d.ts.map +0 -1
  874. package/dist/shell/page-bar.test.d.ts +0 -2
  875. package/dist/shell/page-bar.test.d.ts.map +0 -1
  876. package/dist/state/fields.test.d.ts +0 -2
  877. package/dist/state/fields.test.d.ts.map +0 -1
  878. package/dist/styles.floor.test.d.ts +0 -2
  879. package/dist/styles.floor.test.d.ts.map +0 -1
  880. package/dist/theme/border-coverage.test.d.ts +0 -18
  881. package/dist/theme/border-coverage.test.d.ts.map +0 -1
  882. package/dist/theme/contrast.test.d.ts +0 -2
  883. package/dist/theme/contrast.test.d.ts.map +0 -1
  884. package/dist/theme/ctl-theme.test.d.ts +0 -2
  885. package/dist/theme/ctl-theme.test.d.ts.map +0 -1
  886. package/dist/theme/ctl-tier-coverage.test.d.ts +0 -2
  887. package/dist/theme/ctl-tier-coverage.test.d.ts.map +0 -1
  888. package/dist/theme/density-option.test.d.ts +0 -2
  889. package/dist/theme/density-option.test.d.ts.map +0 -1
  890. package/dist/theme/density-relations.test.d.ts +0 -2
  891. package/dist/theme/density-relations.test.d.ts.map +0 -1
  892. package/dist/theme/derive-theme.test.d.ts +0 -2
  893. package/dist/theme/derive-theme.test.d.ts.map +0 -1
  894. package/dist/theme/divider-law.test.d.ts +0 -2
  895. package/dist/theme/divider-law.test.d.ts.map +0 -1
  896. package/dist/theme/fonts-option.test.d.ts +0 -2
  897. package/dist/theme/fonts-option.test.d.ts.map +0 -1
  898. package/dist/theme/icon-slot.test.d.ts +0 -2
  899. package/dist/theme/icon-slot.test.d.ts.map +0 -1
  900. package/dist/theme/layout-rhythm-css.test.d.ts +0 -2
  901. package/dist/theme/layout-rhythm-css.test.d.ts.map +0 -1
  902. package/dist/theme/radius-option.test.d.ts +0 -2
  903. package/dist/theme/radius-option.test.d.ts.map +0 -1
  904. package/dist/theme/radius.test.d.ts +0 -2
  905. package/dist/theme/radius.test.d.ts.map +0 -1
  906. package/dist/theme/shadow-surfaces.test.d.ts +0 -26
  907. package/dist/theme/shadow-surfaces.test.d.ts.map +0 -1
  908. package/dist/theme/spacing.test.d.ts +0 -2
  909. package/dist/theme/spacing.test.d.ts.map +0 -1
  910. package/dist/theme/use-basalt-spacing.test.d.ts +0 -2
  911. package/dist/theme/use-basalt-spacing.test.d.ts.map +0 -1
  912. package/dist/theme-lab/derive-controls.test.d.ts +0 -2
  913. package/dist/theme-lab/derive-controls.test.d.ts.map +0 -1
  914. package/dist/theme-lab/derive-state.test.d.ts +0 -2
  915. package/dist/theme-lab/derive-state.test.d.ts.map +0 -1
  916. package/dist/theme-lab/theme-lab.test.d.ts +0 -2
  917. package/dist/theme-lab/theme-lab.test.d.ts.map +0 -1
  918. package/dist/tokens/build-fonts-css.test.d.ts +0 -2
  919. package/dist/tokens/build-fonts-css.test.d.ts.map +0 -1
  920. package/dist/tokens/build-radius-css.test.d.ts +0 -2
  921. package/dist/tokens/build-radius-css.test.d.ts.map +0 -1
  922. package/dist/tokens/chart-margin.test.d.ts +0 -2
  923. package/dist/tokens/chart-margin.test.d.ts.map +0 -1
  924. package/dist/tokens/density.test.d.ts +0 -2
  925. package/dist/tokens/density.test.d.ts.map +0 -1
  926. package/dist/tokens/derive-radius.test.d.ts +0 -2
  927. package/dist/tokens/derive-radius.test.d.ts.map +0 -1
  928. package/dist/tokens/derive.test.d.ts +0 -2
  929. package/dist/tokens/derive.test.d.ts.map +0 -1
  930. package/dist/tokens/palette.test.d.ts +0 -2
  931. package/dist/tokens/palette.test.d.ts.map +0 -1
  932. package/dist/tokens/text-scale.test.d.ts +0 -2
  933. package/dist/tokens/text-scale.test.d.ts.map +0 -1
  934. package/dist/vite-color-mix.test.d.ts +0 -2
  935. package/dist/vite-color-mix.test.d.ts.map +0 -1
  936. package/dist/vite.test.d.ts +0 -2
  937. package/dist/vite.test.d.ts.map +0 -1
  938. package/dist/widget-header/delta-badge.test.d.ts +0 -2
  939. package/dist/widget-header/delta-badge.test.d.ts.map +0 -1
  940. package/dist/widget-header/no-mantine.test.d.ts +0 -2
  941. package/dist/widget-header/no-mantine.test.d.ts.map +0 -1
  942. package/dist/widget-header/widget-header.test.d.ts +0 -2
  943. package/dist/widget-header/widget-header.test.d.ts.map +0 -1
  944. package/src/agent/adapter.test.ts +0 -1640
  945. package/src/agent/ai-sdk-transport.test.ts +0 -658
  946. package/src/agent/coalesce.test.ts +0 -387
  947. package/src/agent/foreign.test.ts +0 -51
  948. package/src/agent/id.test.ts +0 -169
  949. package/src/agent/merge.test.ts +0 -206
  950. package/src/agent/part-list.test.tsx +0 -112
  951. package/src/agent/parts.test.ts +0 -720
  952. package/src/agent/thread.test.ts +0 -220
  953. package/src/agent/transport.test.ts +0 -70
  954. package/src/agent/use-agent-stream.test.tsx +0 -287
  955. package/src/agent/use-agent-thread-runs.foreign.test.tsx +0 -138
  956. package/src/agent/use-agent-thread-runs.resume.test.tsx +0 -294
  957. package/src/agent/use-agent-thread-runs.test.tsx +0 -1120
  958. package/src/agent/use-agent-thread-runs.wedge.test.tsx +0 -387
  959. package/src/agent-chat/composer.test.tsx +0 -924
  960. package/src/agent-chat/relative-time.test.ts +0 -95
  961. package/src/agent-chat/thread-feed-row.test.tsx +0 -510
  962. package/src/agent-chat/thread-feed.test.tsx +0 -241
  963. package/src/agent-chat/thread-message.test.tsx +0 -1285
  964. package/src/agent-chat/thread-workspace.test.tsx +0 -169
  965. package/src/agent-chat/tool-chip.test.tsx +0 -139
  966. package/src/agent-chat/virtualize.type-guard.test.ts +0 -49
  967. package/src/charts/cursor/resolve.test.ts +0 -151
  968. package/src/charts/cursor/store.test.ts +0 -64
  969. package/src/charts/kinds/BandStrip.test.tsx +0 -282
  970. package/src/charts/kinds/Bars.test.tsx +0 -183
  971. package/src/charts/kinds/DualPanel.test.tsx +0 -527
  972. package/src/charts/kinds/MirroredBars.test.tsx +0 -232
  973. package/src/charts/kinds/MultiLine.test.tsx +0 -218
  974. package/src/charts/kinds/StackedArea.test.tsx +0 -114
  975. package/src/charts/kinds/ZonedLine.test.tsx +0 -147
  976. package/src/charts/layout/auto-margin.test.ts +0 -42
  977. package/src/charts/primitives/CartesianChart.test.tsx +0 -647
  978. package/src/charts/primitives/ChartCard.test.tsx +0 -66
  979. package/src/charts/primitives/ChartFrame.test.tsx +0 -142
  980. package/src/charts/primitives/ChartLegend.test.tsx +0 -78
  981. package/src/charts/primitives/ChartTooltip.test.tsx +0 -44
  982. package/src/charts/primitives/XZoneRects.test.tsx +0 -123
  983. package/src/charts/series.test.ts +0 -112
  984. package/src/charts/utils/domain.test.ts +0 -54
  985. package/src/charts/utils/measure-text.test.ts +0 -36
  986. package/src/cli/check-coverage.test.ts +0 -297
  987. package/src/cli/check-theme.test.ts +0 -837
  988. package/src/cli/help.test.ts +0 -170
  989. package/src/cli/init-sync.test.ts +0 -343
  990. package/src/cli/placement-engine.test.ts +0 -298
  991. package/src/cli/shipped-versions.test.ts +0 -83
  992. package/src/cli/tarball-parity.test.ts +0 -44
  993. package/src/cli/template-format.test.ts +0 -64
  994. package/src/cli/template-ledger.test.ts +0 -219
  995. package/src/cli/tokens-css.test.ts +0 -249
  996. package/src/cli/toolchain-wiring.test.ts +0 -1358
  997. package/src/cli/waiver-audit.test.ts +0 -215
  998. package/src/content/article-actions.test.ts +0 -78
  999. package/src/content/article-model.test.ts +0 -119
  1000. package/src/content/block-split.test.ts +0 -75
  1001. package/src/content/heading-components.test.tsx +0 -65
  1002. package/src/content/markdown.test.tsx +0 -603
  1003. package/src/content/prose.module.css.test.ts +0 -39
  1004. package/src/content/sanitize.test.ts +0 -659
  1005. package/src/content/slug.test.ts +0 -62
  1006. package/src/controls/actions.test.tsx +0 -252
  1007. package/src/controls/actions.type-guard.test.ts +0 -63
  1008. package/src/controls/control-group.test.tsx +0 -229
  1009. package/src/controls/controls.router.test.tsx +0 -637
  1010. package/src/controls/filter-set.test.tsx +0 -472
  1011. package/src/controls/peer-boundary.test.ts +0 -85
  1012. package/src/controls/props-contract.test.tsx +0 -106
  1013. package/src/controls/sync-button.test.tsx +0 -183
  1014. package/src/dashboard/query-state.test.tsx +0 -152
  1015. package/src/dashboard/settings-section.test.tsx +0 -56
  1016. package/src/dashboard/stat-card.test.tsx +0 -154
  1017. package/src/data/data-table.test.tsx +0 -434
  1018. package/src/guard/check-source.test.ts +0 -3355
  1019. package/src/guard/grace.test.ts +0 -149
  1020. package/src/guard/guard-hook.test.ts +0 -203
  1021. package/src/provider/index.test.tsx +0 -101
  1022. package/src/provider/lab-theme.test.ts +0 -147
  1023. package/src/query/error-message.test.ts +0 -67
  1024. package/src/router-tanstack/multi-search-param-store.test.ts +0 -124
  1025. package/src/router-tanstack/nav.test.ts +0 -111
  1026. package/src/router-tanstack/search-param-store.test.ts +0 -135
  1027. package/src/router-tanstack/search-store.router.test.tsx +0 -661
  1028. package/src/router-tanstack/search-store.test.ts +0 -418
  1029. package/src/router-tanstack/use-nav.test.tsx +0 -187
  1030. package/src/section/section.test.tsx +0 -204
  1031. package/src/shell/app-mobile-nav.test.tsx +0 -403
  1032. package/src/shell/app-sidebar.test.tsx +0 -509
  1033. package/src/shell/index.test.tsx +0 -428
  1034. package/src/shell/mobile-nav-model.test.ts +0 -517
  1035. package/src/shell/page-bar.test.tsx +0 -316
  1036. package/src/state/fields.test.tsx +0 -258
  1037. package/src/styles.floor.test.ts +0 -36
  1038. package/src/theme/border-coverage.test.ts +0 -272
  1039. package/src/theme/contrast.test.ts +0 -354
  1040. package/src/theme/ctl-theme.test.tsx +0 -275
  1041. package/src/theme/ctl-tier-coverage.test.ts +0 -170
  1042. package/src/theme/density-option.test.ts +0 -96
  1043. package/src/theme/density-relations.test.ts +0 -506
  1044. package/src/theme/derive-theme.test.ts +0 -101
  1045. package/src/theme/divider-law.test.ts +0 -259
  1046. package/src/theme/fonts-option.test.ts +0 -75
  1047. package/src/theme/icon-slot.test.tsx +0 -275
  1048. package/src/theme/layout-rhythm-css.test.ts +0 -72
  1049. package/src/theme/radius-option.test.ts +0 -86
  1050. package/src/theme/radius.test.ts +0 -142
  1051. package/src/theme/shadow-surfaces.test.ts +0 -488
  1052. package/src/theme/spacing.test.ts +0 -495
  1053. package/src/theme/use-basalt-spacing.test.tsx +0 -55
  1054. package/src/theme-lab/derive-controls.test.ts +0 -94
  1055. package/src/theme-lab/derive-state.test.ts +0 -71
  1056. package/src/theme-lab/theme-lab.test.ts +0 -46
  1057. package/src/tokens/build-fonts-css.test.ts +0 -52
  1058. package/src/tokens/build-radius-css.test.ts +0 -25
  1059. package/src/tokens/chart-margin.test.ts +0 -39
  1060. package/src/tokens/density.test.ts +0 -259
  1061. package/src/tokens/derive-radius.test.ts +0 -62
  1062. package/src/tokens/derive.test.ts +0 -30
  1063. package/src/tokens/palette.test.ts +0 -114
  1064. package/src/tokens/text-scale.test.ts +0 -51
  1065. package/src/vite-color-mix.test.ts +0 -46
  1066. package/src/vite.test.ts +0 -423
  1067. package/src/widget-header/delta-badge.test.tsx +0 -60
  1068. package/src/widget-header/no-mantine.test.ts +0 -33
  1069. package/src/widget-header/widget-header.test.tsx +0 -187
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Typed CSS-module declaration (the S4 CSS-modules path). Named string props — not an index
3
+ * signature — so dot access stays verbatim under the package's strict flags.
4
+ */
5
+ declare const classes: {
6
+ readonly row: string
7
+ readonly head: string
8
+ readonly label: string
9
+ readonly readout: string
10
+ readonly end: string
11
+ readonly control: string
12
+ }
13
+ export default classes
@@ -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,21 +27,50 @@
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
 
52
+ /** Once per label — a filter renders on every navigation, and the wiring is fixed at definition. */
53
+ const noPickerWarned = new Set<string>()
54
+
55
+ /**
56
+ * Dev-only: the field declares `custom: true` and no picker was injected, so the custom window is
57
+ * unreachable — the popover shows presets only and the sheet's `Custom range…` row never renders.
58
+ * A warning rather than a type error: `custom: true` with presets only is a legal (and tested)
59
+ * configuration, and the picker is INJECTED precisely because `@mantine/dates` may be absent, so
60
+ * the type cannot tell "deliberately preset-only" from "forgot the import".
61
+ */
62
+ function warnCustomWithoutPicker(label: string, unreachable: boolean): void {
63
+ if (!unreachable || !isDev() || noPickerWarned.has(label)) return
64
+ noPickerWarned.add(label)
65
+ // oxlint-disable-next-line no-console -- a dev-time wiring warning has no other channel
66
+ console.warn(
67
+ `[basalt-ui] RangeFilter('${label}'): the field declares \`custom: true\` but no ` +
68
+ '`customPicker` was injected, so the custom window cannot be reached. Pass ' +
69
+ '`customPicker={DateRangePicker}` (`basalt-ui/controls-dates`), or drop `custom: true` from ' +
70
+ 'the field. (dev only)',
71
+ )
72
+ }
73
+
44
74
  /**
45
75
  * The contract a custom-range picker satisfies — `basalt-ui/controls-dates`' `DateRangePicker`, or a
46
76
  * consumer's own. Dates are ISO (`YYYY-MM-DD`), the same shape `field.range` stores and
@@ -51,28 +81,59 @@ export type RangeCustomPickerProps = {
51
81
  readonly onChange: (next: { from: string; to: string }) => void
52
82
  }
53
83
 
54
- export type RangeFilterProps<P extends string> = {
55
- readonly field: FieldHandle<RangeField<P>>
84
+ /** Everything about the props that does not depend on the field's `custom` flag. */
85
+ type RangeFilterBase<P extends string, C extends boolean> = BasaltProps & {
86
+ /**
87
+ * A range handle from either store factory. Generic over the field's `custom` flag so a
88
+ * `field.range({ ... })` WITHOUT `custom` (whose handle is `RangeField<P, false>`, and whose
89
+ * values therefore never include `'custom'`) binds with no cast — pinning `C` to its default
90
+ * `boolean` here made the setter contravariantly incompatible and forced one at every call site.
91
+ */
92
+ readonly field: FieldHandle<RangeField<P, C>>
56
93
  /** Leading pill icon. Defaults to a calendar glyph — a range filter reads as a date control, so
57
94
  * the glyph is part of the control's identity, not a per-call-site decision. */
58
95
  readonly icon?: ReactNode
59
- /** Rendered when the field declares `custom: true`. Omitted → presets only. */
60
- readonly customPicker?: ComponentType<RangeCustomPickerProps>
61
96
  /** Sheet-form heading. The pill's own label is the VALUE, which is what a bar reads. @default 'Range' */
62
97
  readonly label?: string
63
98
  }
64
99
 
65
- export function RangeFilter<P extends string>({
66
- field,
67
- icon,
68
- customPicker,
69
- label = 'Range',
70
- }: RangeFilterProps<P>): ReactNode {
100
+ export type RangeFilterProps<P extends string, C extends boolean = boolean> = RangeFilterBase<
101
+ P,
102
+ C
103
+ > & {
104
+ /**
105
+ * Rendered when the field declares `custom: true`. Omitted → presets only, which stays LEGAL
106
+ * (a range that only ever shows presets is a real configuration) but warns once in dev when the
107
+ * field allows a custom window: the affordance is then unreachable, and nothing else says so.
108
+ */
109
+ readonly customPicker?: ComponentType<RangeCustomPickerProps>
110
+ }
111
+
112
+ export function RangeFilter<P extends string, C extends boolean = boolean>(
113
+ props: RangeFilterProps<P, C>,
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
+ })
120
+ const {
121
+ field,
122
+ icon,
123
+ customPicker,
124
+ label = 'Range',
125
+ className,
126
+ style,
127
+ // One cast, at the one boundary a generic `C` cannot be read through: `C` only ever describes
128
+ // the FIELD's custom flag, and the body handles both.
129
+ } = props as unknown as RangeFilterBase<P, boolean> & {
130
+ readonly customPicker?: ComponentType<RangeCustomPickerProps>
131
+ }
71
132
  const [value, setValue] = field.use()
72
133
  const surface = useFilterSurface()
73
134
  const isDefault = field.isDefault(value)
74
135
  useFilterRegistration(!isDefault, () => {
75
- setValue(field.fallback)
136
+ field.clear()
76
137
  })
77
138
 
78
139
  const options = field.options
@@ -80,48 +141,75 @@ export function RangeFilter<P extends string>({
80
141
  const allowsCustom = options.length !== presets.length
81
142
  const numeric = presets.length > 0 && presets.every((option) => NUMERIC_LABEL.test(option.label))
82
143
  const inSheet = surface === 'sheet'
144
+ const inPanel = surface === 'panel'
83
145
  const Picker = customPicker
146
+ warnCustomWithoutPicker(label, allowsCustom && Picker === undefined)
84
147
  // The track is a `radiogroup`; without this it announces unnamed, and the pill it hangs off reads
85
148
  // the VALUE (`30d`), not the filter.
86
- const { labelId, nameProps } = useControlName(label, inSheet)
149
+ const { labelId, nameProps } = useControlName(label, inSheet || inPanel)
87
150
  // Expanded when the field ALREADY holds a custom window — a reader who deep-linked one must see
88
151
  // it without hunting for the row that holds it. Local, because it is an overlay's open flag and
89
152
  // not a filter value (the same line `FilterPill`'s `opened` state draws).
90
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])
91
166
  const showPicker = allowsCustom && Picker !== undefined
92
167
 
93
- 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) {
94
174
  return (
95
- <SheetField label={label} labelId={labelId}>
96
- <SheetOptionList
97
- mode="single"
98
- labelId={labelId}
99
- selected={[value.preset]}
100
- options={presets.map((option) => ({ value: option.value, label: option.label }))}
101
- 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
+ }
102
198
  setCustomOpen(false)
103
199
  // Rendered from `field.options`, so `next` is always a declared preset — the cast
104
200
  // restores what the codec already guarantees.
105
201
  setValue({ preset: next } as RangeValue<P | 'custom'>)
106
202
  }}
107
203
  />
108
- {showPicker && (
109
- <SheetDisclosure
110
- label="Custom range…"
111
- expanded={customOpen}
112
- onToggle={() => {
113
- 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'>)
114
209
  }}
115
- >
116
- <Picker
117
- value={{ from: value.from, to: value.to }}
118
- onChange={({ from, to }) => {
119
- setValue({ preset: 'custom', from, to } as RangeValue<P | 'custom'>)
120
- }}
121
- />
122
- </SheetDisclosure>
210
+ />
123
211
  )}
124
- </SheetField>
212
+ </PanelRow>
125
213
  )
126
214
  }
127
215
 
@@ -157,6 +245,8 @@ export function RangeFilter<P extends string>({
157
245
  active={!isDefault}
158
246
  numeric={numeric && value.preset !== 'custom'}
159
247
  icon={icon ?? <CalendarGlyph />}
248
+ {...(className !== undefined && { className })}
249
+ {...(style !== undefined && { style })}
160
250
  >
161
251
  <div className={classes.optionList}>{body}</div>
162
252
  </FilterPill>