bitboss-ui 3.0.0-beta.63 → 3.0.0-beta.64

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 (377) hide show
  1. package/README.md +25 -20
  2. package/bin/bitboss-ui.mjs +73 -7
  3. package/dist/ai/BbAlert.md +10 -1
  4. package/dist/ai/BbBadge.md +35 -3
  5. package/dist/ai/BbBadgeButton.md +4 -2
  6. package/dist/ai/BbBaseButton.md +9 -3
  7. package/dist/ai/BbBreadcrumbs.md +2 -2
  8. package/dist/ai/BbButton.md +113 -8
  9. package/dist/ai/BbCheckbox.md +1 -1
  10. package/dist/ai/BbCheckboxGroup.md +94 -6
  11. package/dist/ai/BbColorInput.md +4 -1
  12. package/dist/ai/BbColorPalette.md +2 -1
  13. package/dist/ai/BbConfirm.md +7 -3
  14. package/dist/ai/BbDatePicker.md +2 -1
  15. package/dist/ai/BbDatePickerInput.md +4 -1
  16. package/dist/ai/BbDialog.md +12 -11
  17. package/dist/ai/BbDropdown.md +189 -38
  18. package/dist/ai/BbDropdownButton.md +27 -7
  19. package/dist/ai/BbForm.md +55 -22
  20. package/dist/ai/BbHost.md +2 -2
  21. package/dist/ai/BbHoverCard.md +7 -3
  22. package/dist/ai/BbIcon.md +12 -6
  23. package/dist/ai/BbIndicator.md +11 -1
  24. package/dist/ai/BbNumberInput.md +4 -1
  25. package/dist/ai/BbOffCanvas.md +37 -3
  26. package/dist/ai/BbPopover.md +2 -1
  27. package/dist/ai/BbRadio.md +1 -1
  28. package/dist/ai/BbRadioGroup.md +102 -10
  29. package/dist/ai/BbSelect.md +52 -17
  30. package/dist/ai/BbSelectPopover.md +93 -26
  31. package/dist/ai/BbSwitch.md +1 -1
  32. package/dist/ai/BbSwitchGroup.md +30 -3
  33. package/dist/ai/BbTable.md +17 -10
  34. package/dist/ai/BbTabs.md +8 -3
  35. package/dist/ai/BbTabsList.md +4 -1
  36. package/dist/ai/BbTag.md +5 -2
  37. package/dist/ai/BbTextInput.md +4 -1
  38. package/dist/ai/BbTextarea.md +4 -1
  39. package/dist/ai/BbTimePicker.md +2 -1
  40. package/dist/ai/BbTimePickerInput.md +4 -1
  41. package/dist/ai/BbToast.md +9 -3
  42. package/dist/ai/BbTooltip.md +71 -9
  43. package/dist/ai/ChipsBox.md +1 -1
  44. package/dist/ai/CommonField.md +1 -1
  45. package/dist/ai/OptionsContainer.md +42 -0
  46. package/dist/ai/changelog.json +416 -6
  47. package/dist/ai/components.json +50 -48
  48. package/dist/ai/composables/useConfirm.md +39 -1
  49. package/dist/ai/design-tokens.json +6 -6
  50. package/dist/ai/guides/agent-contract.md +2 -2
  51. package/dist/ai/guides/ai-router.md +24 -23
  52. package/dist/ai/guides/app-layout.md +5 -0
  53. package/dist/ai/guides/component-picker.md +9 -2
  54. package/dist/ai/guides/design-language.md +24 -24
  55. package/dist/ai/guides/design-tokens.md +15 -8
  56. package/dist/ai/guides/icons-policy.md +9 -2
  57. package/dist/ai/guides/inertia-helpers.md +79 -49
  58. package/dist/ai/guides/installation-and-plugin-setup.md +12 -2
  59. package/dist/ai/guides/migration/adoption-checklist.md +235 -0
  60. package/dist/ai/guides/migration/components/bb-checkbox-group.md +16 -15
  61. package/dist/ai/guides/migration/components/bb-dialog.md +9 -9
  62. package/dist/ai/guides/migration/components/bb-dropdown.md +7 -2
  63. package/dist/ai/guides/migration/components/bb-popover.md +4 -2
  64. package/dist/ai/guides/migration/components/bb-select.md +19 -0
  65. package/dist/ai/guides/migration/components/bb-table.md +1 -0
  66. package/dist/ai/guides/migration/components/bb-toast.md +4 -0
  67. package/dist/ai/guides/migration/components/bb-tooltip.md +15 -12
  68. package/dist/ai/guides/migration/components/use-confirm.md +25 -2
  69. package/dist/ai/guides/migration/v2-to-v3.md +163 -9
  70. package/dist/ai/guides/migration/v3-beta-css-classes.md +7 -3
  71. package/dist/ai/guides/options-items-playbook.md +2 -1
  72. package/dist/ai/guides/passthrough.md +147 -85
  73. package/dist/ai/guides/preset-components.md +140 -15
  74. package/dist/ai/helpers/definePreset.json +7 -0
  75. package/dist/ai/helpers/definePreset.ts +123 -11
  76. package/dist/ai/helpers/inertia/manifest.json +15 -9
  77. package/dist/ai/helpers/inertia/useConfirmOnRouteLeave.ts +4 -4
  78. package/dist/ai/helpers/inertia/usePaginatedResponseContext.ts +39 -24
  79. package/dist/ai/helpers/inertia/useRouteQuery.ts +10 -4
  80. package/dist/ai/index.md +1 -0
  81. package/dist/ai/manifest/common.json +0 -1
  82. package/dist/ai/manifest/components/BbAlert.json +7 -2
  83. package/dist/ai/manifest/components/BbBadge.json +13 -4
  84. package/dist/ai/manifest/components/BbBadge.tree.json +2 -0
  85. package/dist/ai/manifest/components/BbBadgeButton.json +4 -2
  86. package/dist/ai/manifest/components/BbBaseButton.json +4 -2
  87. package/dist/ai/manifest/components/BbBreadcrumbs.tree.json +1 -1
  88. package/dist/ai/manifest/components/BbButton.json +14 -3
  89. package/dist/ai/manifest/components/BbButton.tree.json +5 -1
  90. package/dist/ai/manifest/components/BbCheckboxGroup.json +13 -8
  91. package/dist/ai/manifest/components/BbCheckboxGroup.tree.json +11 -1
  92. package/dist/ai/manifest/components/BbColorInput.json +18 -15
  93. package/dist/ai/manifest/components/BbColorPalette.json +1 -1
  94. package/dist/ai/manifest/components/BbConfirm.json +4 -2
  95. package/dist/ai/manifest/components/BbConfirm.tree.json +1 -1
  96. package/dist/ai/manifest/components/BbDatePicker.json +1 -1
  97. package/dist/ai/manifest/components/BbDatePickerInput.json +17 -14
  98. package/dist/ai/manifest/components/BbDialog.tree.json +2 -1
  99. package/dist/ai/manifest/components/BbDropdown.json +24 -19
  100. package/dist/ai/manifest/components/BbDropdown.tree.json +9 -2
  101. package/dist/ai/manifest/components/BbDropdownButton.json +33 -20
  102. package/dist/ai/manifest/components/BbDropdownButton.tree.json +2 -2
  103. package/dist/ai/manifest/components/BbForm.json +2 -2
  104. package/dist/ai/manifest/components/BbHost.tree.json +1 -1
  105. package/dist/ai/manifest/components/BbHoverCard.json +1 -1
  106. package/dist/ai/manifest/components/BbIndicator.json +8 -2
  107. package/dist/ai/manifest/components/BbNumberInput.json +5 -2
  108. package/dist/ai/manifest/components/BbOffCanvas.json +2 -2
  109. package/dist/ai/manifest/components/BbOffCanvas.tree.json +1 -1
  110. package/dist/ai/manifest/components/BbPopover.json +1 -1
  111. package/dist/ai/manifest/components/BbRadioGroup.json +17 -12
  112. package/dist/ai/manifest/components/BbRadioGroup.tree.json +8 -1
  113. package/dist/ai/manifest/components/BbSelect.json +21 -18
  114. package/dist/ai/manifest/components/BbSelect.tree.json +2 -2
  115. package/dist/ai/manifest/components/BbSelectPopover.json +10 -10
  116. package/dist/ai/manifest/components/BbSelectPopover.tree.json +3 -2
  117. package/dist/ai/manifest/components/BbSwitchGroup.json +1 -1
  118. package/dist/ai/manifest/components/BbSwitchGroup.tree.json +3 -1
  119. package/dist/ai/manifest/components/BbTable.tree.json +14 -1
  120. package/dist/ai/manifest/components/BbTabs.json +9 -6
  121. package/dist/ai/manifest/components/BbTabs.tree.json +1 -1
  122. package/dist/ai/manifest/components/BbTabsList.json +6 -3
  123. package/dist/ai/manifest/components/BbTag.json +19 -16
  124. package/dist/ai/manifest/components/BbTag.tree.json +1 -1
  125. package/dist/ai/manifest/components/BbTextInput.json +6 -3
  126. package/dist/ai/manifest/components/BbTextarea.json +6 -3
  127. package/dist/ai/manifest/components/BbTimePicker.json +1 -1
  128. package/dist/ai/manifest/components/BbTimePickerInput.json +10 -7
  129. package/dist/ai/manifest/components/BbToast.json +7 -2
  130. package/dist/ai/manifest/components/BbToast.tree.json +4 -2
  131. package/dist/ai/manifest/components/BbTooltip.json +6 -5
  132. package/dist/ai/manifest/components/ChipsBox.tree.json +1 -1
  133. package/dist/ai/manifest/components/CommonField.json +1 -1
  134. package/dist/ai/manifest/components/OptionsContainer.json +3 -1
  135. package/dist/ai/manifest/components/OptionsContainer.tree.json +38 -0
  136. package/dist/ai/manifest/index.json +3 -3
  137. package/dist/ai/manifest/meta.json +5 -3
  138. package/dist/ai/manifest/types.advanced.json +5 -2
  139. package/dist/ai/manifest/types.api.json +9 -7
  140. package/dist/ai/recipes/inertia/layout-scaffold.md +19 -6
  141. package/dist/ai/recipes/inertia/records-workspace.md +49 -13
  142. package/dist/ai/recipes/nuxt/layout-scaffold.md +19 -6
  143. package/dist/ai/recipes/nuxt/records-workspace.md +42 -12
  144. package/dist/ai/recipes/vue/layout-scaffold.md +19 -6
  145. package/dist/ai/recipes/vue/records-workspace.md +42 -12
  146. package/dist/ai/source/BbAlert.md +94 -76
  147. package/dist/ai/source/BbBadge.md +174 -72
  148. package/dist/ai/source/BbBadgeButton.md +130 -65
  149. package/dist/ai/source/BbBaseButton.md +46 -124
  150. package/dist/ai/source/BbBaseCheckboxIcon.md +38 -0
  151. package/dist/ai/source/BbBaseRadioIcon.md +18 -0
  152. package/dist/ai/source/BbBaseSwitchIcon.md +51 -0
  153. package/dist/ai/source/BbButton.md +153 -36
  154. package/dist/ai/source/BbCheckboxGroup.md +142 -18
  155. package/dist/ai/source/BbColorInput.md +240 -204
  156. package/dist/ai/source/BbColorPalette.md +7 -4
  157. package/dist/ai/source/BbConfirm.md +34 -11
  158. package/dist/ai/source/BbConfirmPortal.md +24 -9
  159. package/dist/ai/source/BbDatePicker.md +11 -7
  160. package/dist/ai/source/BbDatePickerInput.md +306 -269
  161. package/dist/ai/source/BbDialog.md +49 -14
  162. package/dist/ai/source/BbDropdown.md +314 -197
  163. package/dist/ai/source/BbDropdownButton.md +197 -31
  164. package/dist/ai/source/BbDropdownGroup.md +274 -176
  165. package/dist/ai/source/BbForm.md +50 -6
  166. package/dist/ai/source/BbHoverCard.md +12 -5
  167. package/dist/ai/source/BbIcon.md +4 -3
  168. package/dist/ai/source/BbIndicator.md +73 -52
  169. package/dist/ai/source/BbNumberInput.md +293 -257
  170. package/dist/ai/source/BbOffCanvas.md +55 -29
  171. package/dist/ai/source/BbPopover.md +14 -10
  172. package/dist/ai/source/BbRadioGroup.md +413 -297
  173. package/dist/ai/source/BbSelect.md +549 -435
  174. package/dist/ai/source/BbSelectPopover.md +128 -33
  175. package/dist/ai/source/BbSwitchGroup.md +70 -10
  176. package/dist/ai/source/BbTable.md +369 -37
  177. package/dist/ai/source/BbTabs.md +115 -39
  178. package/dist/ai/source/BbTabsList.md +106 -38
  179. package/dist/ai/source/BbTabsPanels.md +96 -36
  180. package/dist/ai/source/BbTabsRoot.md +96 -36
  181. package/dist/ai/source/BbTag.md +267 -233
  182. package/dist/ai/source/BbTextInput.md +261 -225
  183. package/dist/ai/source/BbTextarea.md +251 -215
  184. package/dist/ai/source/BbTimePicker.md +11 -7
  185. package/dist/ai/source/BbTimePickerInput.md +257 -220
  186. package/dist/ai/source/BbToast.md +73 -32
  187. package/dist/ai/source/BbToastPortal.md +65 -30
  188. package/dist/ai/source/BbTooltip.md +148 -113
  189. package/dist/ai/source/CommonPopover.md +6 -1
  190. package/dist/ai/source/FlatListBox.md +19 -9
  191. package/dist/ai/source/GroupedListBox.md +19 -9
  192. package/dist/ai/source/OptionsContainer.md +25 -9
  193. package/dist/components/BbAlert/BbAlert.vue_vue_type_script_setup_true_lang.js +61 -36
  194. package/dist/components/BbAlert/types.d.ts +9 -5
  195. package/dist/components/BbAlert/types.js +8 -3
  196. package/dist/components/BbBadge/BadgeBodyContent.vue.d.ts +3 -3
  197. package/dist/components/BbBadge/BadgeBodyContent.vue_vue_type_script_setup_true_lang.js +26 -17
  198. package/dist/components/BbBadge/BbBadge.vue_vue_type_script_setup_true_lang.js +103 -50
  199. package/dist/components/BbBadge/BbBadgeButton.vue_vue_type_script_setup_true_lang.js +20 -15
  200. package/dist/components/BbBadge/badgeButtonContext.d.ts +12 -1
  201. package/dist/components/BbBadge/types.d.ts +45 -15
  202. package/dist/components/BbBadge/types.js +12 -3
  203. package/dist/components/BbBaseButton/BbBaseButton.vue_vue_type_script_setup_true_lang.js +89 -126
  204. package/dist/components/BbBaseButton/types.d.ts +23 -4
  205. package/dist/components/BbBaseButton/types.js +7 -1
  206. package/dist/components/BbBaseButton/useLinkState.d.ts +41 -0
  207. package/dist/components/BbBaseButton/useLinkState.js +70 -0
  208. package/dist/components/BbButton/BbButton.vue_vue_type_script_setup_true_lang.js +130 -74
  209. package/dist/components/BbButton/types.d.ts +50 -13
  210. package/dist/components/BbButton/types.js +10 -3
  211. package/dist/components/BbCheckboxGroup/BbCheckboxGroup.vue_vue_type_script_setup_true_lang.js +148 -95
  212. package/dist/components/BbCheckboxGroup/types.d.ts +34 -11
  213. package/dist/components/BbCheckboxGroup/types.js +9 -4
  214. package/dist/components/BbCollapsible/BbCollapsible.vue.d.ts +1 -1
  215. package/dist/components/BbColorInput/BbColorInput.vue_vue_type_script_setup_true_lang.js +134 -93
  216. package/dist/components/BbColorInput/types.d.ts +11 -4
  217. package/dist/components/BbColorInput/types.js +9 -3
  218. package/dist/components/BbConfirm/BbConfirm.vue_vue_type_script_setup_true_lang.js +82 -59
  219. package/dist/components/BbConfirm/types.d.ts +9 -4
  220. package/dist/components/BbConfirm/types.js +8 -3
  221. package/dist/components/BbDatePicker/BbDatePicker.vue.d.ts +1 -1
  222. package/dist/components/BbDatePicker/BbDatePicker.vue_vue_type_script_setup_true_lang.js +68 -67
  223. package/dist/components/BbDatePickerInput/BbDatePickerInput.vue.d.ts +1 -1
  224. package/dist/components/BbDatePickerInput/BbDatePickerInput.vue_vue_type_script_setup_true_lang.js +259 -215
  225. package/dist/components/BbDatePickerInput/types.d.ts +12 -5
  226. package/dist/components/BbDatePickerInput/types.js +9 -3
  227. package/dist/components/BbDialog/BbDialog.vue_vue_type_script_setup_true_lang.js +223 -219
  228. package/dist/components/BbDialog/useDialogStack.d.ts +1 -1
  229. package/dist/components/BbDropdown/AdaptiveDropdown.vue_vue_type_script_setup_true_lang.js +104 -89
  230. package/dist/components/BbDropdown/BbDropdown.vue.d.ts +1 -0
  231. package/dist/components/BbDropdown/BbDropdown.vue_vue_type_script_setup_true_lang.js +323 -285
  232. package/dist/components/BbDropdown/BbDropdownList.vue.d.ts +1 -0
  233. package/dist/components/BbDropdown/BbDropdownList.vue_vue_type_script_setup_true_lang.js +411 -319
  234. package/dist/components/BbDropdown/DropdownPipelineResolver.vue_vue_type_script_setup_true_lang.js +100 -98
  235. package/dist/components/BbDropdown/DropdownRow.vue.d.ts +4 -3
  236. package/dist/components/BbDropdown/DropdownRow.vue_vue_type_script_setup_true_lang.js +49 -28
  237. package/dist/components/BbDropdown/normalizeGroups.d.ts +6 -0
  238. package/dist/components/BbDropdown/normalizeGroups.js +21 -15
  239. package/dist/components/BbDropdown/types.d.ts +59 -12
  240. package/dist/components/BbDropdown/types.js +16 -3
  241. package/dist/components/BbDropdown/useDropdownContext.d.ts +6 -0
  242. package/dist/components/BbDropdownButton/BbDropdownButton.vue.d.ts +2 -1
  243. package/dist/components/BbDropdownButton/BbDropdownButton.vue_vue_type_script_setup_true_lang.js +179 -68
  244. package/dist/components/BbDropdownButton/types.d.ts +44 -16
  245. package/dist/components/BbDropdownButton/types.js +28 -3
  246. package/dist/components/BbForm/BbForm.vue_vue_type_script_setup_true_lang.js +111 -89
  247. package/dist/components/BbForm/formSchema.d.ts +13 -0
  248. package/dist/components/BbHoverCard/BbHoverCard.vue_vue_type_script_setup_true_lang.js +100 -100
  249. package/dist/components/BbIndicator/BbIndicator.vue_vue_type_script_setup_true_lang.js +40 -25
  250. package/dist/components/BbIndicator/types.d.ts +9 -5
  251. package/dist/components/BbIndicator/types.js +8 -3
  252. package/dist/components/BbNumberInput/BbNumberInput.vue_vue_type_script_setup_true_lang.js +243 -202
  253. package/dist/components/BbNumberInput/types.d.ts +12 -5
  254. package/dist/components/BbNumberInput/types.js +9 -3
  255. package/dist/components/BbOffCanvas/BbOffCanvas.vue.d.ts +1 -1
  256. package/dist/components/BbOffCanvas/BbOffCanvas.vue_vue_type_script_setup_true_lang.js +401 -399
  257. package/dist/components/BbOffCanvas/types.d.ts +6 -2
  258. package/dist/components/BbPopover/BbPopover.vue_vue_type_script_setup_true_lang.js +81 -80
  259. package/dist/components/BbRadioGroup/BbRadioGroup.vue_vue_type_script_setup_true_lang.js +158 -105
  260. package/dist/components/BbRadioGroup/types.d.ts +21 -7
  261. package/dist/components/BbRadioGroup/types.js +9 -4
  262. package/dist/components/BbSelect/BbSelect.vue.d.ts +4 -3
  263. package/dist/components/BbSelect/BbSelect.vue_vue_type_script_setup_true_lang.js +181 -137
  264. package/dist/components/BbSelect/types.d.ts +36 -22
  265. package/dist/components/BbSelect/types.js +22 -3
  266. package/dist/components/BbSelectPopover/BbSelectPopover.vue.d.ts +4 -4
  267. package/dist/components/BbSelectPopover/BbSelectPopover.vue_vue_type_script_setup_true_lang.js +543 -532
  268. package/dist/components/BbSelectPopover/knownItems.d.ts +5 -0
  269. package/dist/components/BbSelectPopover/types.d.ts +46 -12
  270. package/dist/components/BbSmoothHeight/BbSmoothHeight.vue.d.ts +1 -1
  271. package/dist/components/BbSwitchGroup/BbSwitchGroup.vue_vue_type_script_setup_true_lang.js +2 -2
  272. package/dist/components/BbSwitchGroup/types.d.ts +14 -4
  273. package/dist/components/BbSwitchGroup/types.js +8 -3
  274. package/dist/components/BbTable/BbTable.vue_vue_type_script_setup_true_lang.js +1102 -995
  275. package/dist/components/BbTable/estimatedFloors.golden.d.ts +25 -0
  276. package/dist/components/BbTable/measuredFloors.d.ts +46 -0
  277. package/dist/components/BbTable/measuredFloors.js +322 -0
  278. package/dist/components/BbTable/measuredFloorsTestKit.d.ts +27 -0
  279. package/dist/components/BbTable/textRuler.d.ts +71 -0
  280. package/dist/components/BbTable/textRuler.js +121 -0
  281. package/dist/components/BbTable/utils.d.ts +113 -6
  282. package/dist/components/BbTable/utils.js +118 -65
  283. package/dist/components/BbTabs/BbTabs.vue_vue_type_script_setup_true_lang.js +53 -35
  284. package/dist/components/BbTabs/BbTabsList.vue_vue_type_script_setup_true_lang.js +62 -45
  285. package/dist/components/BbTabs/types.d.ts +26 -11
  286. package/dist/components/BbTabs/types.js +23 -8
  287. package/dist/components/BbTag/BbTag.vue_vue_type_script_setup_true_lang.js +205 -164
  288. package/dist/components/BbTag/types.d.ts +11 -4
  289. package/dist/components/BbTag/types.js +9 -3
  290. package/dist/components/BbTextInput/BbTextInput.vue_vue_type_script_setup_true_lang.js +98 -57
  291. package/dist/components/BbTextInput/types.d.ts +12 -5
  292. package/dist/components/BbTextInput/types.js +9 -3
  293. package/dist/components/BbTextarea/BbTextarea.vue_vue_type_script_setup_true_lang.js +137 -96
  294. package/dist/components/BbTextarea/types.d.ts +12 -5
  295. package/dist/components/BbTextarea/types.js +9 -3
  296. package/dist/components/BbTimePicker/BbTimePicker.vue_vue_type_script_setup_true_lang.js +105 -104
  297. package/dist/components/BbTimePickerInput/BbTimePickerInput.vue_vue_type_script_setup_true_lang.js +241 -197
  298. package/dist/components/BbTimePickerInput/types.d.ts +12 -5
  299. package/dist/components/BbTimePickerInput/types.js +9 -3
  300. package/dist/components/BbToast/BbToast.vue_vue_type_script_setup_true_lang.js +74 -32
  301. package/dist/components/BbToast/BbToastMessage.vue_vue_type_script_setup_true_lang.js +47 -47
  302. package/dist/components/BbToast/types.d.ts +10 -5
  303. package/dist/components/BbToast/types.js +18 -3
  304. package/dist/components/BbTooltip/BbTooltip.vue_vue_type_script_setup_true_lang.js +85 -71
  305. package/dist/components/BbTooltip/types.d.ts +13 -8
  306. package/dist/components/BbTooltip/types.js +8 -3
  307. package/dist/components/CommonField/CommonField.vue.d.ts +1 -1
  308. package/dist/components/CommonPopover/CommonPopover.vue.d.ts +1 -1
  309. package/dist/components/CommonPopover/CommonPopover.vue_vue_type_script_setup_true_lang.js +69 -69
  310. package/dist/components/FlatListBox/FlatListBox.vue_vue_type_script_setup_true_lang.js +53 -50
  311. package/dist/components/GroupedListBox/GroupedListBox.vue_vue_type_script_setup_true_lang.js +146 -143
  312. package/dist/components/OptionsContainer/OptionsContainer.vue_vue_type_script_setup_true_lang.js +7 -2
  313. package/dist/components/OptionsContainer/types.d.ts +14 -3
  314. package/dist/composables/useBbFormContext.d.ts +4 -3
  315. package/dist/composables/useBbTabsContext.js +99 -85
  316. package/dist/composables/useChoiceIndicator.d.ts +58 -0
  317. package/dist/composables/useChoiceIndicator.js +197 -0
  318. package/dist/composables/useConfirm.d.ts +4 -4
  319. package/dist/composables/usePassthrough.d.ts +54 -1
  320. package/dist/composables/usePassthrough.js +100 -71
  321. package/dist/directives/bbTooltip.d.ts +12 -6
  322. package/dist/directives/createPopoverDirective.js +1 -1
  323. package/dist/index.d.ts +3 -1
  324. package/dist/llms-full.txt +2300 -543
  325. package/dist/llms-medium.txt +71 -53
  326. package/dist/llms.txt +2 -1
  327. package/dist/passthrough-deduper.js +6 -3
  328. package/dist/project-dts.d.ts +24 -0
  329. package/dist/project-dts.js +43 -17
  330. package/dist/styles.css +2 -2
  331. package/dist/types/AlertVariant.d.ts +14 -0
  332. package/dist/types/AlertVariant.js +9 -0
  333. package/dist/types/BadgeVariant.d.ts +14 -0
  334. package/dist/types/BadgeVariant.js +9 -0
  335. package/dist/types/ButtonVariant.d.ts +16 -0
  336. package/dist/types/ButtonVariant.js +11 -0
  337. package/dist/types/CheckboxVariant.d.ts +38 -0
  338. package/dist/types/CheckboxVariant.js +4 -0
  339. package/dist/types/CommonProps.d.ts +4 -3
  340. package/dist/types/Config.d.ts +7 -0
  341. package/dist/types/ConfirmVariant.d.ts +12 -0
  342. package/dist/types/ConfirmVariant.js +4 -0
  343. package/dist/types/DropdownItemVariant.d.ts +12 -0
  344. package/dist/types/DropdownItemVariant.js +4 -0
  345. package/dist/types/IndicatorVariant.d.ts +15 -0
  346. package/dist/types/IndicatorVariant.js +10 -0
  347. package/dist/types/InputVariant.d.ts +13 -0
  348. package/dist/types/InputVariant.js +8 -0
  349. package/dist/types/RadioVariant.d.ts +38 -0
  350. package/dist/types/RadioVariant.js +4 -0
  351. package/dist/types/TabsVariant.d.ts +13 -0
  352. package/dist/types/TabsVariant.js +8 -0
  353. package/dist/types/ToastVariant.d.ts +15 -0
  354. package/dist/types/ToastVariant.js +10 -0
  355. package/dist/types/TooltipVariant.d.ts +12 -0
  356. package/dist/types/TooltipVariant.js +4 -0
  357. package/dist/types/passthrough.d.ts +83 -15
  358. package/dist/types/passthrough.js +2 -2
  359. package/dist/types/ptComponentMap.d.ts +35 -23
  360. package/dist/utilities/functions/extractDomContainer.d.ts +20 -1
  361. package/dist/utilities/functions/extractDomContainer.js +8 -3
  362. package/dist/utils/focusVisibility.d.ts +6 -0
  363. package/dist/utils/focusVisibility.js +4 -1
  364. package/dist/utils/passthroughDeduper.d.ts +14 -0
  365. package/dist/utils/versionCheck.js +1 -1
  366. package/dist/vite-plugin.d.ts +64 -7
  367. package/dist/vite.js +385 -259
  368. package/llms.txt +2 -1
  369. package/package.json +3 -3
  370. package/scripts/lib/adoption-checklist.json +312 -0
  371. package/scripts/lib/eslint-plugin.d.ts +13 -0
  372. package/scripts/lib/eslint-plugin.mjs +75 -11
  373. package/scripts/lib/idiom-scan.mjs +65 -0
  374. package/scripts/lib/mcp-config.mjs +203 -9
  375. package/scripts/lib/migrate-fix.mjs +42 -1
  376. package/scripts/lib/project-variant-dts.mjs +18 -0
  377. package/scripts/lib/validate-bb-markup.mjs +99 -2
@@ -648,6 +648,7 @@ import {
648
648
  reactive,
649
649
  ref,
650
650
  shallowRef,
651
+ toDisplayString,
651
652
  toRaw,
652
653
  toRef,
653
654
  useAttrs,
@@ -739,12 +740,16 @@ import {
739
740
  readResolvedTracks,
740
741
  rowNameText,
741
742
  sameColumnOrder,
743
+ cellRole as valueRoleClasses,
742
744
  estimateFromProfile,
743
745
  fitFloors,
744
746
  profileValues,
747
+ estimateSamples,
748
+ floorTextPx,
745
749
  sampleForEstimate,
746
750
  } from './utils';
747
751
  import type { ColumnProfile, FixedColumnConfig, TrackEstimate } from './utils';
752
+ import { createMeasuredFloors, type MeasuredColumn } from './measuredFloors';
748
753
  import { useBbTableContextHost } from '../../composables/useBbTableContext';
749
754
  import { useLogger } from '@/composables/useLogger';
750
755
  import {
@@ -4275,9 +4280,16 @@ const mapColumnToCell = (
4275
4280
  // Lazy for the same reason as `rowClasses` above: `td-class` is the
4276
4281
  // consumer's function over the consumer's state.
4277
4282
  get classes() {
4283
+ const key = String(column.key);
4278
4284
  return mergeCellClasses(
4279
- fluidColumnKeys.value.has(String(column.key))
4280
- ? [`bb-table__cell--${align}`, 'bb-table__cell--fluid']
4285
+ fluidColumnKeys.value.has(key)
4286
+ ? [
4287
+ `bb-table__cell--${align}`,
4288
+ 'bb-table__cell--fluid',
4289
+ // How the fit holds this value (`cellRole`): static per text,
4290
+ // switched on by the table root's hold level.
4291
+ ...roleClassesOf(key, content()),
4292
+ ]
4281
4293
  : [`bb-table__cell--${align}`],
4282
4294
  props.tdClass,
4283
4295
  column.tdClass,
@@ -4320,6 +4332,19 @@ const mapColumnToCell = (
4320
4332
  * their first batch: a floor there would cut values off rather than wrap
4321
4333
  * them), a custom `#thead`, declared or resized widths, or slot columns (no
4322
4334
  * text to read: their content floors them as before).
4335
+ *
4336
+ * MEASURED FLOORS. The estimate's character table follows Arial; a wider UI
4337
+ * font drew a date 2px past its floor and broke it. Four layers since:
4338
+ * 1. Classification: which values stay whole is the estimate's (`cellRole`,
4339
+ * a static role class per cell), the same on the server.
4340
+ * 2. The plan: after mount the values that could be a column's widest, and
4341
+ * its heading, are measured in their rendered type (measuredFloors.ts,
4342
+ * textRuler.ts); a measured width only RAISES a reservation.
4343
+ * 3. Geometry: the fit's hold level is one class on the grid root, and the
4344
+ * cells it promises floor their own track (index.css), so the browser
4345
+ * keeps them whole with the real text in the real font.
4346
+ * 4. The guard: a plan that says "fits" but overflows reads its held cells
4347
+ * and headers in place and raises the same store (bounded).
4323
4348
  */
4324
4349
  /**
4325
4350
  * What each estimated column shows (its text exactly as the body renders it),
@@ -4328,8 +4353,8 @@ const mapColumnToCell = (
4328
4353
  * like any other. Apart from the estimate, so a resize, which re-runs that,
4329
4354
  * re-reads no rows.
4330
4355
  */
4331
- const columnProfiles = computed(() => {
4332
- const out: Record<string, ColumnProfile | null> = {};
4356
+ const columnValues = computed(() => {
4357
+ const out: Record<string, string[]> = {};
4333
4358
  // A custom `#thead` renders its own header cells: no spacer to carry a
4334
4359
  // floor, and `--fluid` body cells would leave the columns with none.
4335
4360
  if (props.fixed || isInheriting.value || virtualActive.value || slots.thead)
@@ -4338,15 +4363,87 @@ const columnProfiles = computed(() => {
4338
4363
  for (const column of orderedColumns.value) {
4339
4364
  if (effectiveWidth(column) != null) continue;
4340
4365
  if (slots[slotNamesByKey.value[column.key] as keyof typeof slots]) continue;
4341
- out[String(column.key)] = profileValues(
4342
- rows.map((raw) => {
4343
- const content = cellContentOf(column, raw);
4344
- return isNil(content) ? '' : String(content);
4345
- })
4366
+ // The text exactly as the cell renders it (an array or an object too).
4367
+ out[String(column.key)] = rows.map((raw) =>
4368
+ toDisplayString(cellContentOf(column, raw))
4369
+ );
4370
+ }
4371
+ return out;
4372
+ });
4373
+ /** The body text's px, apart from the rest of the metrics: a resize must not
4374
+ * re-profile the columns. */
4375
+ const bodyEm = computed(() => floorMetrics.value?.em ?? 0);
4376
+ /** The values' real widths, measured after mount (measuredFloors.ts). */
4377
+ const measured = createMeasuredFloors({
4378
+ table: () => tableRef.value,
4379
+ container: () => containerRef.value,
4380
+ columns: () => measuredColumns(),
4381
+ em: () => bodyEm.value,
4382
+ plan: () => {
4383
+ const { stage, hold, floors, wrapLabels } = fittedFloors.value;
4384
+ return JSON.stringify([stage, hold, floors, [...wrapLabels]]);
4385
+ },
4386
+ });
4387
+ /** Each column profiled, its reservations raised to the measured widths. */
4388
+ const columnProfiles = computed(() => {
4389
+ void measured.version.value;
4390
+ const out: Record<string, ColumnProfile | null> = {};
4391
+ for (const [key, values] of Object.entries(columnValues.value))
4392
+ out[key] = profileValues(values, measured.lookup(key));
4393
+ return out;
4394
+ });
4395
+
4396
+ /**
4397
+ * What `cellRole` reads of each column, as ONE string first (like
4398
+ * `fluidKeyList`): it changes with the rows only, never with a measurement
4399
+ * or a resize, so the cells re-render only when their roles can change.
4400
+ */
4401
+ const columnRoleKeyList = computed(() => {
4402
+ const parts: string[] = [];
4403
+ for (const [key, p] of Object.entries(columnProfiles.value))
4404
+ if (p) parts.push(`${key}\u0001${p.prose ? 1 : 0}\u0001${p.usual}`);
4405
+ return parts.join('\u0002');
4406
+ });
4407
+ const roleInputs = computed(() => {
4408
+ const out = new Map<string, { prose: boolean; usual: number }>();
4409
+ for (const part of columnRoleKeyList.value.split('\u0002')) {
4410
+ if (!part) continue;
4411
+ const [key, prose, usual] = part.split('\u0001');
4412
+ out.set(key!, { prose: prose === '1', usual: Number(usual) });
4413
+ }
4414
+ return out;
4415
+ });
4416
+ /**
4417
+ * Past 2,000 rows the estimate reads a sample (`sampleForEstimate`): the texts
4418
+ * it read, per column, or `null` when it reads every row. A value only an
4419
+ * unread row shows was never reserved, so it gets no role (fluid, it wraps as
4420
+ * before): held, it would widen its track past the plan. Off the rows and the
4421
+ * columns only (not `columnValues`, which a resize preview re-runs), so a
4422
+ * resize re-renders no cell.
4423
+ */
4424
+ const sampledTexts = computed(() => {
4425
+ const all = internalItems.value ?? [];
4426
+ if (!estimateSamples(all.length)) return null;
4427
+ const rows = sampleForEstimate(all);
4428
+ const out = new Map<string, Set<string>>();
4429
+ for (const column of orderedColumns.value) {
4430
+ if (slots[slotNamesByKey.value[column.key] as keyof typeof slots]) continue;
4431
+ out.set(
4432
+ String(column.key),
4433
+ new Set(
4434
+ rows.map((raw) => toDisplayString(cellContentOf(column, raw)).trim())
4435
+ )
4346
4436
  );
4347
4437
  }
4348
4438
  return out;
4349
4439
  });
4440
+ const roleClassesOf = (key: string, content: unknown): string[] => {
4441
+ const inputs = roleInputs.value.get(key);
4442
+ if (!inputs) return [];
4443
+ const text = toDisplayString(content);
4444
+ if (sampledTexts.value?.get(key)?.has(text.trim()) === false) return [];
4445
+ return valueRoleClasses(text, inputs);
4446
+ };
4350
4447
 
4351
4448
  const columnEstimates = computed(() => {
4352
4449
  const out: Record<string, TrackEstimate> = {};
@@ -4365,29 +4462,25 @@ const columnEstimates = computed(() => {
4365
4462
  const key = String(column.key);
4366
4463
  if (!(key in profiles)) continue;
4367
4464
  // The sort arrow: 1em plus the 4px gap, in the header's type.
4465
+ const arrowEm = column.sortable ? 1.3 : 0;
4368
4466
  const estimate = estimateFromProfile(
4369
4467
  profiles[key]!,
4370
4468
  headerType
4371
4469
  ? casedLabel(column.label ?? '', headerType.transform)
4372
4470
  : (column.label ?? ''),
4373
- padEm,
4374
- column.sortable ? 1.3 : 0,
4471
+ // A body padded wider than its header (a `td-class` `px-6`) reserves
4472
+ // that padding too: the header's spacer then covers it (raise-only).
4473
+ padEm + measured.extraPadOf(key),
4474
+ arrowEm,
4375
4475
  headerType,
4376
- legacyPadEm
4476
+ legacyPadEm,
4477
+ // The heading as it really renders, once measured (raise-only).
4478
+ measured.labelOf(key, arrowEm)
4377
4479
  );
4378
4480
  estimate.squeeze = (estimate.squeeze - legacyPadEm) * legacyScale + padEm;
4379
4481
  // Only our own label wraps (Q76.9 e): a header slot or an affix keeps
4380
4482
  // its heading on one line, so the fit must not count on two.
4381
- const slot = slotNamesByKey.value[column.key];
4382
- if (
4383
- [
4384
- `${slot}:header`,
4385
- `${slot}:header:prepend`,
4386
- `${slot}:header:append`,
4387
- 'header:prepend',
4388
- 'header:append',
4389
- ].some((name) => slots[name as keyof typeof slots])
4390
- ) {
4483
+ if (!ownsHeading(column)) {
4391
4484
  estimate.labelWrap = estimate.label;
4392
4485
  estimate.wrapped = estimate.hard;
4393
4486
  }
@@ -4396,6 +4489,42 @@ const columnEstimates = computed(() => {
4396
4489
  return out;
4397
4490
  });
4398
4491
 
4492
+ /** Our own label renders the heading: no header slot, no affix. */
4493
+ const ownsHeading = (column: BbTableColumn<Item>) => {
4494
+ const slot = slotNamesByKey.value[column.key];
4495
+ return ![
4496
+ `${slot}:header`,
4497
+ `${slot}:header:prepend`,
4498
+ `${slot}:header:append`,
4499
+ 'header:prepend',
4500
+ 'header:append',
4501
+ ].some((name) => slots[name as keyof typeof slots]);
4502
+ };
4503
+ /** What the measuring pass reads of each estimated column (not reactive:
4504
+ * read inside the pass). */
4505
+ const measuredColumns = (): MeasuredColumn[] => {
4506
+ const values = columnValues.value;
4507
+ const offset = Number(rowDragActive.value) + Number(!!props.selectable);
4508
+ const out: MeasuredColumn[] = [];
4509
+ orderedColumns.value.forEach((column, index) => {
4510
+ const key = String(column.key);
4511
+ if (!(key in values)) return;
4512
+ out.push({
4513
+ key,
4514
+ index: index + offset,
4515
+ values: values[key]!,
4516
+ placeholder: column.placeholder || undefined,
4517
+ prose: roleInputs.value.get(key)?.prose ?? false,
4518
+ label: ownsHeading(column) ? column.label || undefined : undefined,
4519
+ dynamic:
4520
+ typeof props.tdClass === 'function' ||
4521
+ typeof column.tdClass === 'function' ||
4522
+ typeof props.rowClass === 'function',
4523
+ });
4524
+ });
4525
+ return out;
4526
+ };
4527
+
4399
4528
  // The `--fluid` body cells, as a string first (see `clippedKeyList`): a new
4400
4529
  // floor or a container resize must not re-render every cell.
4401
4530
  const fluidKeyList = computed(() =>
@@ -4451,6 +4580,17 @@ const floorRoomEm = computed(() => {
4451
4580
  const fittedFloors = computed(() =>
4452
4581
  fitFloors(columnEstimates.value, floorRoomEm.value)
4453
4582
  );
4583
+ /**
4584
+ * The table root's hold level (`fitFloors`' `hold`): which of the values the
4585
+ * estimate holds the floors still cover, so their cells may floor their own
4586
+ * track (index.css). One class on the root, so a new level re-renders no row.
4587
+ */
4588
+ const holdClass = computed(() => {
4589
+ const { hold } = fittedFloors.value;
4590
+ return fluidKeyList.value && hold !== 'none'
4591
+ ? `bb-table__table--hold-${hold}`
4592
+ : undefined;
4593
+ });
4454
4594
  /** Headings given two lines so the table fits (Q76.9 e): `--wrap`. */
4455
4595
  const wrapLabelKeys = computed(() => fittedFloors.value.wrapLabels);
4456
4596
 
@@ -4465,7 +4605,7 @@ const columnFloorStyles = computed(() => {
4465
4605
  const text = Math.max(0, floors[key]! - padEm);
4466
4606
  out[key] = {
4467
4607
  '--floor': metrics
4468
- ? `${Math.floor(text * metrics.em)}px`
4608
+ ? `${floorTextPx(floors[key]!, padEm, metrics.em)}px`
4469
4609
  : // In body-text em like the estimate, whatever the header's type.
4470
4610
  `calc(${text.toFixed(2)} * var(--bb-fs, 1em))`,
4471
4611
  };
@@ -4475,6 +4615,41 @@ const columnFloorStyles = computed(() => {
4475
4615
 
4476
4616
  let floorObserver: ResizeObserver | undefined;
4477
4617
  let floorFrame = 0;
4618
+ /** `--bb-fs` in px, probed once per value of what it resolves from: the
4619
+ * token itself, the root's and the page's font sizes, and the box a viewport
4620
+ * or container unit resolves against (which no style read sees change). */
4621
+ let emProbe: { key: string; em: number } | undefined;
4622
+ /** The sizes of `el`'s query containers, for a `cq` unit's key. */
4623
+ const containerSizes = (el: HTMLElement) => {
4624
+ const sizes: string[] = [];
4625
+ for (let node = el.parentElement; node; node = node.parentElement)
4626
+ if (getComputedStyle(node).containerType !== 'normal')
4627
+ sizes.push(`${node.clientWidth}x${node.clientHeight}`);
4628
+ return sizes.join(',');
4629
+ };
4630
+ const bodyEmOf = (container: HTMLElement) => {
4631
+ const style = getComputedStyle(container);
4632
+ const token = style.getPropertyValue('--bb-fs').trim();
4633
+ const key = [
4634
+ token,
4635
+ style.fontSize,
4636
+ getComputedStyle(document.documentElement).fontSize,
4637
+ // A `cq` unit with no query container resolves against the viewport.
4638
+ /v(w|h|i|b|min|max)|cq/.test(token)
4639
+ ? `${window.innerWidth}x${window.innerHeight}`
4640
+ : '',
4641
+ /cq/.test(token) ? containerSizes(container) : '',
4642
+ ].join('|');
4643
+ if (emProbe?.key === key) return emProbe.em;
4644
+ const probe = document.createElement('span');
4645
+ probe.style.cssText =
4646
+ 'position:absolute;visibility:hidden;font-size:var(--bb-fs,1em)';
4647
+ container.append(probe);
4648
+ const em = parseFloat(getComputedStyle(probe).fontSize);
4649
+ probe.remove();
4650
+ emProbe = { key, em };
4651
+ return em;
4652
+ };
4478
4653
  const measureFloors = () => {
4479
4654
  const container = containerRef.value;
4480
4655
  const table = tableRef.value;
@@ -4483,13 +4658,10 @@ const measureFloors = () => {
4483
4658
  const rootEm = parseFloat(style.fontSize) || 16;
4484
4659
  // The estimates are in em of the BODY text (`--bb-fs`, 14px by default),
4485
4660
  // not the table root's 16px — resolved on a probe, so it holds with no
4486
- // rows yet and whatever a cell class does to one cell's size.
4487
- const probe = document.createElement('span');
4488
- probe.style.cssText =
4489
- 'position:absolute;visibility:hidden;font-size:var(--bb-fs,1em)';
4490
- container.append(probe);
4491
- const em = parseFloat(getComputedStyle(probe).fontSize) || rootEm;
4492
- probe.remove();
4661
+ // rows yet and whatever a cell class does to one cell's size. Only when
4662
+ // what it resolves from changed: any node added under the root (a grid
4663
+ // scroller) re-lays out every row, 100ms and more at 1000 rows.
4664
+ const em = bodyEmOf(container) || rootEm;
4493
4665
  const padEm = (2 * (parseFloat(style.getPropertyValue('--px')) || 12)) / em;
4494
4666
  // The structural columns (drag handle, selection, actions) are
4495
4667
  // `max-content` tracks of a fixed width: not room for the floors.
@@ -4526,23 +4698,84 @@ const measureFloors = () => {
4526
4698
  )
4527
4699
  floorMetrics.value = { width, em, rootEm, padEm, head };
4528
4700
  };
4701
+ /** The cells each hold level promises (index.css), for the guard. */
4702
+ const HELD_CELLS = {
4703
+ units: {
4704
+ whole:
4705
+ '.bb-table__cell--fluid:is(.bb-table__cell--line, .bb-table__cell--token)',
4706
+ word: '.bb-table__cell--fluid.bb-table__cell--word',
4707
+ },
4708
+ core: {
4709
+ whole:
4710
+ '.bb-table__cell--fluid:is(.bb-table__cell--line, .bb-table__cell--token):not(.bb-table__cell--outlier)',
4711
+ word: '.bb-table__cell--fluid.bb-table__cell--word:not(.bb-table__cell--outlier)',
4712
+ },
4713
+ words: {
4714
+ whole: '.bb-table__cell--fluid.bb-table__cell--token',
4715
+ word: '.bb-table__cell--fluid:is(.bb-table__cell--line, .bb-table__cell--word)',
4716
+ },
4717
+ none: {},
4718
+ } as const;
4719
+ /** After the floors of this pass rendered: a plan that fits must fit. */
4720
+ const guardPlan = () => {
4721
+ const { stage, hold, floors } = fittedFloors.value;
4722
+ if (stage >= 7 || !Object.keys(floors).length) return;
4723
+ measured.guard(hold, HELD_CELLS[hold]);
4724
+ };
4725
+ /**
4726
+ * One pass of the floors: the room and the em (`measureFloors`), a check of
4727
+ * the columns' typography (style reads only), the texts the rows brought
4728
+ * (the ruler, outside the table), then — once Vue has rendered what that
4729
+ * changed — the guard. `mount`: the first pass, before the first paint.
4730
+ */
4731
+ const floorPass = (mount = false) => {
4732
+ measureFloors();
4733
+ if (virtualActive.value) return;
4734
+ measured.checkTypography();
4735
+ measured.measure(mount);
4736
+ nextTick(guardPlan);
4737
+ };
4529
4738
  // Read on the frame AFTER a resize, never inside the callback nor
4530
4739
  // synchronously: a new floor re-lays the table out, which resizes the
4531
4740
  // container again within the same observer loop (the browser reports that as
4532
4741
  // an error), and a forced layout at mount shifts what virtual rows measure.
4533
4742
  const scheduleFloors = () => {
4534
4743
  cancelAnimationFrame(floorFrame);
4535
- floorFrame = requestAnimationFrame(measureFloors);
4744
+ floorFrame = requestAnimationFrame(() => floorPass());
4536
4745
  };
4537
4746
  onMounted(() => {
4538
4747
  if (!containerRef.value || typeof ResizeObserver === 'undefined') return;
4539
4748
  // A client render reaches its first paint measured (the fitted floors and
4540
- // the headings' lines already set), not at the wish for one frame. Not
4541
- // under `virtual` (no floors there): a forced layout at mount shifts what
4542
- // its rows measure.
4543
- if (!virtualActive.value) measureFloors();
4749
+ // the headings' lines already set, the values' real widths read), not at
4750
+ // the wish for one frame. Not under `virtual` (no floors there): a forced
4751
+ // layout at mount shifts what its rows measure.
4752
+ if (!virtualActive.value) floorPass(true);
4544
4753
  floorObserver = new ResizeObserver(scheduleFloors);
4545
4754
  floorObserver.observe(containerRef.value);
4755
+ // The grid itself: held cells widen it when a font changes under them
4756
+ // (no event fires for every font), and the guard then looks. Not under
4757
+ // `virtual` (no floors, no held cells): its rows resize the grid as it
4758
+ // scrolls, and every pass forces a layout.
4759
+ if (tableRef.value && !virtualActive.value)
4760
+ floorObserver.observe(tableRef.value);
4761
+ measured.onFontsChanged(() => !virtualActive.value && scheduleFloors());
4762
+ });
4763
+ watch([tableRef, virtualActive], ([table, virtual], [previous]) => {
4764
+ if (!floorObserver) return;
4765
+ if (previous) floorObserver.unobserve(previous);
4766
+ if (table && !virtual) floorObserver.observe(table);
4767
+ });
4768
+ // New rows: their new texts are measured on the next pass. Only when a
4769
+ // column shows other texts: `columnValues` also re-runs when nothing it
4770
+ // shows changed (a resize preview sizes a column, a consumer passes an equal
4771
+ // `columns` array again), and a re-measure walks every candidate.
4772
+ const sameTexts = (a: readonly string[] | undefined, b: readonly string[]) =>
4773
+ a?.length === b.length && a.every((text, i) => text === b[i]);
4774
+ watch(columnValues, (now, old) => {
4775
+ if (Object.entries(now).every(([key, texts]) => sameTexts(old?.[key], texts)))
4776
+ return;
4777
+ measured.rowsChanged();
4778
+ if (floorObserver) scheduleFloors();
4546
4779
  });
4547
4780
  // The root element swaps (`selectable` turns it into a fieldset): observe
4548
4781
  // the new one, or the floors stay on the last read of a detached box. A
@@ -4560,6 +4793,7 @@ watch(
4560
4793
  onBeforeUnmount(() => {
4561
4794
  floorObserver?.disconnect();
4562
4795
  cancelAnimationFrame(floorFrame);
4796
+ measured.dispose();
4563
4797
  });
4564
4798
 
4565
4799
  /**
@@ -7157,7 +7391,7 @@ const tableBindings = computed(() => {
7157
7391
  ...mergeProps(
7158
7392
  {
7159
7393
  ref: tableRef,
7160
- class: 'bb-table__table',
7394
+ class: ['bb-table__table', holdClass.value],
7161
7395
  role: tableRole.value,
7162
7396
  'aria-labelledby': props.caption ? captionId.value : undefined,
7163
7397
  'aria-multiselectable':
@@ -9987,6 +10221,22 @@ export type BbTableSlots<Item = any> = BbTableColumnSlots<Item> & {
9987
10221
  background-color: var(--row-bg);
9988
10222
  }
9989
10223
 
10224
+ /* Q76.7 (b): on hover a pinned cell takes the row's OWN fill, so an
10225
+ app's `pt:row` hover colour (`hover:bg-*`) reaches it as it reaches
10226
+ the rest of the row (it kept the library's tint before). The row
10227
+ then has to be opaque for the pinned cell to stay opaque (Q56.24 b),
10228
+ so the library's own hover is forced opaque on a row that has one;
10229
+ an opaque theme renders exactly as before. A translucent app hover
10230
+ colour shows through the pinned cell while the row is hovered: give
10231
+ it an opaque one. */
10232
+ &:has(> [style*='sticky']):hover {
10233
+ background-color: rgb(from var(--row-bg) r g b / 1);
10234
+
10235
+ > [style*='sticky'] {
10236
+ background-color: inherit;
10237
+ }
10238
+ }
10239
+
9990
10240
  /* A click opens it (`v-model:highlighted`, `highlightable`). */
9991
10241
  &--highlightable {
9992
10242
  cursor: pointer;
@@ -10055,7 +10305,8 @@ export type BbTableSlots<Item = any> = BbTableColumnSlots<Item> & {
10055
10305
 
10056
10306
  /* Q9.1b: in a column with an estimated floor (see `--floored` above) the
10057
10307
  floor is the minimum, not the longest word: a value too long for it
10058
- wraps, or breaks, rather than widening every row. */
10308
+ wraps, or breaks, rather than widening every row. The values the fit
10309
+ promises are held instead, by the root's hold level (below). */
10059
10310
  &--fluid {
10060
10311
  min-width: 0;
10061
10312
  }
@@ -10085,6 +10336,50 @@ export type BbTableSlots<Item = any> = BbTableColumnSlots<Item> & {
10085
10336
  }
10086
10337
  }
10087
10338
 
10339
+ /* Measured floors: the values the fit promises (`cellRole`: `--line`,
10340
+ `--token`, `--word`, `--outlier`) floor their own track again, at the
10341
+ hold level the fit reached (`fitFloors`' `hold`, one class on the grid
10342
+ root). The browser then sizes the track with the real text in its real
10343
+ font: a promised value never breaks, whatever the estimate said, and the
10344
+ measured floors make sure it never widens a track past the plan. A whole
10345
+ value also keeps to one line (`nowrap`: a hyphen or a slash would break
10346
+ a date under `min-width: auto` alone); a `--word` value only keeps its
10347
+ longest word. Child combinators only: a nested table's cells follow
10348
+ their own root. Internal classes, not public API (like `--fluid`). */
10349
+ &__table--hold-units
10350
+ > .bb-table__body
10351
+ > [role='row']
10352
+ > .bb-table__cell--fluid:is(.bb-table__cell--line, .bb-table__cell--token),
10353
+ &__table--hold-core
10354
+ > .bb-table__body
10355
+ > [role='row']
10356
+ > .bb-table__cell--fluid:is(
10357
+ .bb-table__cell--line,
10358
+ .bb-table__cell--token
10359
+ ):not(.bb-table__cell--outlier),
10360
+ &__table--hold-words
10361
+ > .bb-table__body
10362
+ > [role='row']
10363
+ > .bb-table__cell--fluid.bb-table__cell--token {
10364
+ min-width: auto;
10365
+ white-space: nowrap;
10366
+ }
10367
+
10368
+ &__table--hold-units
10369
+ > .bb-table__body
10370
+ > [role='row']
10371
+ > .bb-table__cell--fluid.bb-table__cell--word,
10372
+ &__table--hold-core
10373
+ > .bb-table__body
10374
+ > [role='row']
10375
+ > .bb-table__cell--fluid.bb-table__cell--word:not(.bb-table__cell--outlier),
10376
+ &__table--hold-words
10377
+ > .bb-table__body
10378
+ > [role='row']
10379
+ > .bb-table__cell--fluid:is(.bb-table__cell--line, .bb-table__cell--word) {
10380
+ min-width: auto;
10381
+ }
10382
+
10088
10383
  /* The grid's roving tab stop (`keyboardNavigation`, role="grid" /
10089
10384
  "treegrid"): cells, header cells and a treegrid's expand rows take
10090
10385
  focus. Same inset ring as the rows and the reorderable headers —
@@ -10284,4 +10579,41 @@ export type BbTableSlots<Item = any> = BbTableColumnSlots<Item> & {
10284
10579
  --indent: 0px;
10285
10580
  --indent-at: select;
10286
10581
  }
10582
+
10583
+ /* The text ruler (textRuler.ts): measured content floors read each held
10584
+ value's real width here, outside every table (a node under a table's grid
10585
+ root would re-lay out all its rows). Off-screen, isolated, never painted;
10586
+ each wrapper carries a rendered cell's computed text properties inline,
10587
+ and its lines inherit them whatever a consumer rule says. Internal, not
10588
+ public API. */
10589
+ .bb-table-ruler {
10590
+ contain: strict;
10591
+ height: 0;
10592
+ inset-inline-start: 0;
10593
+ overflow: hidden;
10594
+ pointer-events: none;
10595
+ position: absolute;
10596
+ top: 0;
10597
+ visibility: hidden;
10598
+ width: 0;
10599
+
10600
+ > * > * {
10601
+ border: 0 !important;
10602
+ direction: inherit !important;
10603
+ display: block !important;
10604
+ font: inherit !important;
10605
+ font-feature-settings: inherit !important;
10606
+ font-variation-settings: inherit !important;
10607
+ letter-spacing: inherit !important;
10608
+ margin: 0 !important;
10609
+ max-width: none !important;
10610
+ min-width: 0 !important;
10611
+ padding: 0 !important;
10612
+ text-rendering: inherit !important;
10613
+ text-transform: inherit !important;
10614
+ white-space: nowrap !important;
10615
+ width: max-content !important;
10616
+ word-spacing: inherit !important;
10617
+ }
10618
+ }
10287
10619
  ```