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
@@ -52,7 +52,10 @@ An `items` entry is one of three shapes, and you can mix them freely:
52
52
  - a **separator section** — a nested `[ … ]` array, rendered as its own
53
53
  divider-separated anonymous group;
54
54
  - a **group object** — `{ id, label?, items, selectable?, … }`, a titled and/or
55
- selectable section (next sections). A group's heading is its `label` too;
55
+ selectable section (next sections). A group with plain rows and no
56
+ `selectable` needs `group: true` to be a group rather than a submenu row;
57
+ [Titled sections](#titled-sections-group-true-or-groupby) compares it with
58
+ `groupBy`, which titles the rows of one group. A group's heading is its `label` too;
56
59
  add `hideLabel` to keep the `label` for the adaptive sheet title / chips /
57
60
  `useBbDropdownContext` while rendering no heading in the menu.
58
61
 
@@ -89,6 +92,10 @@ check` flags both (`missing-value`, `duplicate-row-label`) when the items are
89
92
  rendering a `bb-dropdown__item--variant-<name>` class hook you style yourself.
90
93
  - `disabled` — non-interactive row, skipped by keyboard navigation.
91
94
 
95
+ Nothing else on an item reaches the row. An attribute for it (a `data-*` test
96
+ hook, an `aria-*`) goes through `pt:item`, per row with the function form:
97
+ `:pt:item="({ item }) => ({ attrs: { 'data-test': item.value } })"`.
98
+
92
99
  Note how the destructive item below follows the design rule end to end: its
93
100
  `onClick` awaits [useConfirm](./composables/useConfirm.md) and acts only on a
94
101
  yes (`<BbHost />` mounts once per app, at the root).
@@ -212,9 +219,9 @@ trigger (unless the action moved focus elsewhere, e.g. opened a dialog). When
212
219
  the `onClick` returns a promise, the dropdown keeps tracking it after the menu
213
220
  has closed: the `activator` slot's `loading` flag stays `true` until it
214
221
  settles, and re-clicks on that item are ignored while it's in flight. Reflect
215
- `loading` on your trigger and you get a busy state for free. Inside a
216
- selectable group the menu stays open instead, and the row itself carries
217
- `aria-busy` while its handler runs.
222
+ `loading` on your trigger and you get a busy state for free. A selectable
223
+ row closes the menu only as `closeOnSelect` says (see _Selectable groups_),
224
+ and the row itself carries `aria-busy` while its handler runs.
218
225
 
219
226
  **Background sync with a busy trigger**
220
227
 
@@ -285,8 +292,11 @@ Give an item its own `items` array and it opens a submenu — recursively, to an
285
292
  depth. A chevron is shown automatically (it replaces any `append:icon`). On
286
293
  desktop, submenus are hover-intent flyouts (opening waits for the parent
287
294
  panel's transition; leaving tolerates a short diagonal move); clicking a parent
288
- row opens its submenu and never closes the menu. On the mobile sheet the same
289
- items **drill down** one level at a time with a back control instead. Use
295
+ row opens its submenu with focus inside (its search box, when it has one) and
296
+ never closes the menu, and `Escape` closes one level at a time (see
297
+ [Accessibility](#accessibility) for every key). On the mobile sheet the same
298
+ items **drill down** one level at a time with a back control instead
299
+ (`Escape` is the same back step). Use
290
300
  submenus to group rarely-used options without crowding the top level; keep the
291
301
  nesting shallow (two levels is plenty).
292
302
 
@@ -388,10 +398,16 @@ Behavior you can rely on:
388
398
 
389
399
  - a **single** group is a radio group: re-clicking the selected row keeps it
390
400
  (the row's `onClick` still runs, no `item:selected` / `item:unselected`
391
- fires). Add `clearable: true` to the group to make that re-click clear it.
392
- When the group is the sole top-level selectable group, picking a value
393
- (the same one included) also closes the menu;
401
+ fires). Add `clearable: true` to the group to make that re-click clear it;
402
+ - a change in a **single** group closes the menu and puts focus back on the
403
+ trigger: a pick (the same value included) or a `clearable` group's clear,
404
+ wherever the group sits (inside a submenu too) and whatever else the menu
405
+ holds — a filter menu with a "Role" list and a "Deleted" on/off entry
406
+ closes after either;
394
407
  - a **multiple** group keeps the menu open while you toggle;
408
+ - `close-on-select` overrides both: `true` closes after every selection
409
+ change (a multiple pick included), `false` never closes on a selection.
410
+ Plain actions close the menu either way;
395
411
  - the dropdown emits `item:selected` / `item:unselected` with the item's
396
412
  **value** (its `itemValue` for pipeline groups, else its own `value`) when the user
397
413
  picks or unpicks a row, never for your own changes to a group's value — the
@@ -410,8 +426,7 @@ const statusGroup = {
410
426
  ```
411
427
 
412
428
  - The group is still a selectable group (a function counts as `true`): checks,
413
- `menuitemcheckbox` / `menuitemradio` roles, the menu stays open while
414
- toggling.
429
+ `menuitemcheckbox` / `menuitemradio` roles, and the same closing rules.
415
430
  - A row the function rejects renders **disabled**: clicks do nothing and
416
431
  keyboard navigation skips it.
417
432
  - The lock holds on a row that is **already selected** — it cannot be
@@ -598,6 +613,54 @@ rows distinct values when it matters:
598
613
  - **Submenu parents.** A row that opens a submenu needs a value unique in the
599
614
  whole menu; the flyout is addressed by it.
600
615
 
616
+ ### Titled sections: `group: true` or `groupBy`
617
+
618
+ Two ways to put headings in a menu, for two different needs:
619
+
620
+ - **Independent sections, each its own group: `group: true`.** A group object
621
+ with `group: true` is a titled section (its `label` is the heading) without
622
+ being selectable; give it `selectable` and it becomes its own chooser. Each
623
+ group keeps **its own** value (its own companion and `v-model`) and, when
624
+ `searchable`, its own search field. Use it for sections that answer
625
+ different questions ("Sort by" / "Show").
626
+ - **One choice, sorted under headings: `groupBy` on one group.** The rows of a
627
+ single group are listed under section headings, and it stays one group:
628
+ **one** `v-model`, **one** search field. Use it when the headings only
629
+ organise the options of one question (categories under their sector).
630
+
631
+ ```vue
632
+ <BbDropdown
633
+ :items="[
634
+ {
635
+ id: 'category',
636
+ label: 'Category',
637
+ selectable: true,
638
+ searchable: true,
639
+ groupBy: 'sector',
640
+ items: [
641
+ { value: 1, label: 'Bakery', sector: 'Food' },
642
+ { value: 2, label: 'Clothing', sector: 'No food' },
643
+ { value: 3, label: 'Discount' },
644
+ ],
645
+ },
646
+ ]"
647
+ >
648
+ <BbDropdownGroup id="category" v-model="category" />
649
+ </BbDropdown>
650
+ ```
651
+
652
+ - `groupBy` is a field path (`'sector'`, `'owner.team'`) read off each row,
653
+ or a function `(row) => string | null | undefined`. On a fetched group it
654
+ reads the **raw item**, so `items: ({ query }) => …` groups the same way.
655
+ - Sections keep the order of their first row. Rows whose `groupBy` gives
656
+ `null`, `undefined` or `''` have no section: they are listed **first**,
657
+ before every heading — the same rule as `BbSelect`'s `group-by`.
658
+ - A heading is not a row: the arrows and typeahead skip it, and it is never
659
+ selected. A search hides a section left without a matching row, heading
660
+ included.
661
+ - Each titled section is a `role="group"` labelled by its heading, with the
662
+ look of a group heading. The adaptive sheet shows the same sections.
663
+
601
664
  ### Loading options on demand
602
665
 
603
666
  For options that come from an API, make the group a **pipeline group**: pass an
@@ -633,8 +696,9 @@ The full grammar:
633
696
  - `loadingText` / `emptyText` / `failedText` — placeholder row text per
634
697
  group; the dropdown-level `loading-text` / `empty-text` / `failed-text` props
635
698
  set the default for all groups, and the `#loading` / `#empty` slots (payload:
636
- `{ text, groupId }`, `#empty` also `error`) replace the placeholder rows
637
- entirely.
699
+ `{ text, groupId, query }`, `#empty` also `error`) replace the placeholder
700
+ rows entirely. `query` is the group's search text (`''` without one), like
701
+ `BbSelect`'s `#empty="{ query }"`.
638
702
  - `dependencies: [...]` + `depsDebounceTime` — re-run the getter (debounced)
639
703
  when any dependency changes, e.g. a search field feeding the fetch.
640
704
  - `enforceCoherence` — after a re-fetch, drop selected values that no longer
@@ -1112,6 +1176,30 @@ What each knob does:
1112
1176
  `searchable`, which is the one place the three-state `prefill` differs from a
1113
1177
  plain menu group (`'interaction'`, the default, fetches when the group is
1114
1178
  first reached; `true` fetches on mount).
1179
+ - **A minimum query length** is the getter's call and the `#empty` slot's
1180
+ copy: return `[]` below it, and read `query` in the slot. The slot also
1181
+ replaces the search-first hint, so one condition covers both:
1182
+
1183
+ ```vue
1184
+ <BbDropdown
1185
+ :items="[
1186
+ {
1187
+ id: 'zone',
1188
+ label: 'Zone',
1189
+ searchable: true,
1190
+ selectable: true,
1191
+ prefill: false,
1192
+ filterBy: false,
1193
+ items: ({ query }) => (query.length < 2 ? [] : api.zones({ q: query })),
1194
+ },
1195
+ ]"
1196
+ >
1197
+ <template #empty="{ query, text }">
1198
+ {{ query.length < 2 ? 'Type at least 2 characters' : text }}
1199
+ </template>
1200
+ </BbDropdown>
1201
+ ```
1202
+
1115
1203
  - **`stash`** accumulates the items behind _selected_ keys so they stay
1116
1204
  resolvable after the query changes. Without it, selecting in one search and
1117
1205
  then searching for something else leaves the earlier selection unresolvable
@@ -1597,7 +1685,8 @@ const items: BbDropdownItem[] = [
1597
1685
  - `placement` (default `'bottom'`) accepts every floating-UI placement
1598
1686
  (`top`, `bottom-start`, `right-end`, …) plus `'cursor'`; the panel flips
1599
1687
  automatically when there's no room, and the item list caps its height to the
1600
- available space (scrolling inside).
1688
+ available space (scrolling inside). It is read at open: a change while open
1689
+ applies at the next open.
1601
1690
  - `width` — `'auto'` (default) sizes to content with a 128px floor; a number
1602
1691
  is pixels; a percentage is relative to the **anchor's width**, so
1603
1692
  `width="100%"` makes the menu match its trigger.
@@ -1767,7 +1856,11 @@ the same floor the select's own sheet holds.
1767
1856
  On viewports below the config's `mobileMaxWidth`, the menu renders as a
1768
1857
  bottom-anchored, draggable, auto-sized off-canvas sheet instead of a flyout.
1769
1858
  Nested submenus become drill-down levels with a back control (the parent
1770
- item's text becomes the level title); selection, slots and events behave
1859
+ item's text becomes the level title, and the control points toward the
1860
+ inline start, mirrored under `dir="rtl"`). `Escape` on a drilled-in level, from
1861
+ a row or the level's search box, does what the back control does: back to the
1862
+ parent level, focus on the row that opened it. At the top level it closes the
1863
+ sheet. Selection, slots and events behave
1771
1864
  exactly as on desktop. The surface is **latched per open**: resizing
1772
1865
  mid-session never swaps a live menu between flyout and sheet.
1773
1866
 
@@ -1893,13 +1986,21 @@ const tags = ref<string[]>(['overdue']);
1893
1986
  </script>
1894
1987
  ```
1895
1988
 
1896
- A look that depends on the ENTRY'S DATA — a destructive action, an entry
1897
- flagged by the row it came from — is a **per-item function** on an entry
1898
- part: `:pt:item="({ item, selected, disabled }) => …"`, called with the menu
1899
- entry and every state as a boolean for it, its result merged after the state entries. Entry
1900
- parts only (`item`, `itemText`, `itemIcon`, `itemDescription`), on the
1901
- instance only; `variant: 'destructive'` on the item is still the shipped look
1902
- for the common case — the function is for a look of your own.
1989
+ An entry's `variant` is a state of the entry parts: `pt:item:destructive`
1990
+ (or `'item:destructive'` in the object form and the global map) styles the
1991
+ destructive entries only, after the base `item` entry and before `selected` /
1992
+ `disabled`. So a base look never hides the red: a global
1993
+ `item: 'text-ink hover:bg-tint'` plus `'item:destructive': 'text-red-700'`
1994
+ keeps it, on the flyout and the phone sheet alike. An entry with no `variant`
1995
+ is `default`; a name of your own (`dropdownItemVariants: ['warning']`) works in
1996
+ every form, `pt:item:warning` included.
1997
+
1998
+ A look that depends on the ENTRY'S DATA — an entry flagged by the row it came
1999
+ from — is a **per-item function** on an entry part:
2000
+ `:pt:item="({ item, variant, selected, disabled }) => …"`, called with the menu
2001
+ entry, every state as a boolean for it and its `variant` as a string, its
2002
+ result merged after the state entries. Entry parts only (`item`, `itemText`,
2003
+ `itemIcon`, `itemDescription`), on the instance only.
1903
2004
 
1904
2005
  **pt:item as a function — a destructive entry by its data**
1905
2006
 
@@ -2077,6 +2178,22 @@ A `pt` value written in `<script>` (a constant, a preset, a wrapper's
2077
2178
  `pt` prop) is typed as `BbDropdownProps['pt']`, never an imported `PtValue`: see
2078
2179
  [passthrough § Typing `pt` in `<script>`](./guides/passthrough.md#typing-pt-in-script).
2079
2180
 
2181
+ #### Item text and icon size
2182
+
2183
+ Two knobs on the menu set the rows' size, with BbButton's names: `--fs` the
2184
+ label (default `var(--bb-fs)`, the same as BbSelect's options) and
2185
+ `--icon-size` the prepend / append icons (default `1rem`). Submenus follow
2186
+ them. A denser desktop menu with 13px text and 14px icons:
2187
+
2188
+ ```vue
2189
+ <BbDropdown class="[--fs:13px] [--icon-size:14px]" :items="items" />
2190
+ ```
2191
+
2192
+ They size the desktop menu only. The phone sheet keeps its touch sizes (larger
2193
+ text and icons in 44px rows) and never reads them, so there is no desktop-only
2194
+ switch to add. A component inside a row (a `BbBadge` in an item slot) keeps its
2195
+ own sizes.
2196
+
2080
2197
  ### Works well with
2081
2198
 
2082
2199
  - `BbDropdownButton` — the same `items` model behind a split button when one
@@ -2120,6 +2237,10 @@ A `pt` value written in `<script>` (a constant, a preset, a wrapper's
2120
2237
  a check in `item:append`). Make the group `selectable` and bind its
2121
2238
  `v-model` to the current id — the check, `aria-checked` and the radio role
2122
2239
  come with it.
2240
+ - **A server-searched group needs `filterBy: false`.** The default local
2241
+ match runs on the getter's answer too, so a row the server matched on a field
2242
+ the menu doesn't show (an email, a fiscal code) is fetched and then hidden.
2243
+ See [Searchable groups](#searchable-groups).
2123
2244
  - `width` defaults to `'auto'` (content-sized, ≥128px) — not a fixed pixel
2124
2245
  width; pass a number when menus must align across rows.
2125
2246
  - Item `variant`s other than `destructive` render only a class hook — register
@@ -2160,29 +2281,55 @@ A `pt` value written in `<script>` (a constant, a preset, a wrapper's
2160
2281
  after the row that opened it; and the adaptive sheet, already a `<dialog>`,
2161
2282
  just steps its menu container aside rather than nesting a second dialog.
2162
2283
  Surfaces with no search field are unchanged.
2163
- - Keyboard is fully handled — don't add your own handlers: `ArrowDown` /
2164
- `ArrowUp` on the trigger open the menu focusing the first/last item; inside,
2165
- arrows rove (skipping disabled rows, wrapping), `Home`/`End` jump, typing
2166
- a letter jumps to the next enabled item whose label starts with it
2167
- (typeahead, like a native menu), `ArrowRight` enters a submenu, `ArrowLeft` closes the level (or navigates
2168
- back in the sheet), `Escape` closes and refocuses the trigger. The menu is a
2169
- single tab stop (rows are `tabindex="-1"` per the ARIA menu pattern): `Tab`
2170
- leaves the menu and closes it. Focus returns to the activator after an
2171
- action unless the action moved it (e.g. opened a dialog).
2284
+ - Keyboard is fully handled, following the WAI-ARIA APG menu pattern — don't
2285
+ add your own handlers (table below). The menu is a single tab stop (rows are
2286
+ `tabindex="-1"` per the ARIA menu pattern): `Tab` leaves the menu and closes
2287
+ it. Focus returns to the activator after an action unless the action moved
2288
+ it (e.g. opened a dialog).
2289
+ - A submenu opener carries `aria-haspopup="menu"`, or `"dialog"` when the
2290
+ submenu has a search field, `aria-expanded`, and `aria-controls` naming the
2291
+ submenu while it is open; the submenu is named after its opener. Under `dir="rtl"` submenus open to the left, their
2292
+ chevron points left, and the two inline arrows swap.
2172
2293
  - The focused row's highlight is keyboard-only (`:focus-visible`). The mobile
2173
2294
  sheet always places real focus on the level's first row when it opens — a
2174
2295
  modal dialog would otherwise skip the `tabindex="-1"` rows — but a menu
2175
2296
  opened by tap or click paints nothing, so it never looks pre-selected.
2176
2297
 
2298
+ #### Keyboard
2299
+
2300
+ | Key | Where | What happens |
2301
+ | ------------------------------ | --------------------------------------- | ----------------------------------------------------------------------------- |
2302
+ | `ArrowDown` / `ArrowUp` | trigger | open the menu, focus on the first / last row |
2303
+ | `ArrowDown` / `ArrowUp` | row | next / previous enabled row of the same level, wrapping |
2304
+ | `ArrowDown` / `ArrowUp` | search box | first / last row of its level |
2305
+ | `Home` / `End` | row | first / last of the level (`Home` lands on its search box, when it has one) |
2306
+ | a letter | row | the level's next enabled row whose label starts with what was typed |
2307
+ | `ArrowRight`, `Enter`, `Space` | submenu opener | open the submenu, focus on its search box or first row |
2308
+ | click | submenu opener | the same: open it with focus inside |
2309
+ | hover | submenu opener | open it; focus never moves into it |
2310
+ | `ArrowLeft` | row in a submenu | close that level, focus on its opener (the search box keeps it for the caret) |
2311
+ | `Escape` | anywhere in a submenu (row, search box) | close that level only, focus on its opener: one level per press |
2312
+ | `Escape` | top level | close the menu, focus on the trigger |
2313
+ | pointer leaves the menu | focus inside a submenu | the submenu closes, focus lands on its opener |
2314
+ | `Tab` | inside | leave the menu, which closes |
2315
+
2316
+ `ArrowLeft` / `ArrowRight` read in the row's direction: swapped under
2317
+ `dir="rtl"`. A popover or dialog opened from an item takes `Escape` first;
2318
+ the menu gets it back once that closes. On the mobile sheet the drill-down
2319
+ uses `ArrowRight` / `ArrowLeft` and its back control; `Escape` on a drilled-in
2320
+ level (row or search box) goes back one level, focus on the row that opened
2321
+ it, and at the top level closes the sheet.
2322
+
2177
2323
  ## Props
2178
2324
 
2179
2325
  | Prop | Type | Default | Required | Description |
2180
2326
  | --- | --- | --- | --- | --- |
2181
- | `pt:<part>`, `pt:<part>:<state>`, `pt` | `PtValue` | | | Passthrough — a class list, or `{ class, style, attrs }`, bound to one named part of the component; the state form applies only while that state is on. Parts: `footer`, `header`, `input`, `item`, `itemDescription`, `itemIcon`, `itemText`, `list`, `panel`, `root`, `sheet`. States: `disabled`, `open`, `selected`. What each one is: _Passthrough parts and states_ below. `pt` is the object form with the same keys minus the prefix (`{ icon: '…', 'icon:loading': '…' }`). See `guides/passthrough.md`. In `<script>` a `pt` value is typed `BbDropdownProps['pt']` (`PtValue` is not exported): § Typing `pt` in `<script>`. |
2327
+ | `pt:<part>`, `pt:<part>:<state>`, `pt` | `PtValue` | | | Passthrough — a class list, or `{ class, style, attrs }`, bound to one named part of the component; the state form applies only while that state is on. Parts: `footer`, `header`, `input`, `item`, `itemDescription`, `itemIcon`, `itemText`, `list`, `panel`, `root`, `sheet`. States: `activeLink`, `disabled`, `exactActiveLink`, `open`, `selected`, `default`, `destructive`. What each one is: _Passthrough parts and states_ below. `pt` is the object form with the same keys minus the prefix (`{ icon: '…', 'icon:loading': '…' }`). See `guides/passthrough.md`. In `<script>` a `pt` value is typed `BbDropdownProps['pt']` (`PtValue` is not exported): § Typing `pt` in `<script>`. |
2182
2328
  | `activator` | `Record<string, any> \| HTMLElement \| null \| undefined` | | | An external activator element or component ref: the activator slot is not rendered, and the listeners and ARIA (`aria-haspopup` / `aria-expanded` / `aria-controls`) are attached to it; don't add your own. |
2183
2329
  | `adaptive` | `boolean \| undefined` | ``config.adaptive` (`true`)` | | On mobile (below `config.mobileMaxWidth`), renders the menu as a bottom sheet that drills into submenus one level at a time instead of hover flyouts; slots and selection are unchanged. Unset: the global `config.adaptive`. |
2184
2330
  | `anchor` | `Record<string, any> \| HTMLElement \| null \| undefined` | `the activator element` | | The element the panel positions against when it should differ from the activator (a wrapper the activator is part of); an HTMLElement or component ref. It also drives a percentage `width`. Ignored with `placement="cursor"`. |
2185
2331
  | `boundary` | `string \| Record<string, any> \| HTMLElement \| null \| undefined` | | | Keeps the panel inside this element alongside its trigger (sideways for a menu above or below); away from the trigger it may pass the edge, and only the viewport flips it. An HTMLElement, a component ref or a CSS selector: a `useTemplateRef`, never `$refs.x` (not reactive). |
2332
+ | `closeOnSelect` | `boolean \| undefined` | `unset (close after a single-choice change)` | | Whether a selection closes the menu. Unset: a change in a single-choice group closes it (an untick too, at any depth); a `multiple` group stays open. `true`: every selection change closes it. `false`: none does. |
2186
2333
  | `compact` | `boolean \| undefined` | `false` | | Compact density, the `BbSelect` ladder: flyout rows 28px → 24px, sheet rows 44px → 36px (a sheet row stays a touch target). The label size does not change. |
2187
2334
  | `disabled` | `boolean \| undefined` | `false` | | Disables the component |
2188
2335
  | `eager` | `boolean \| undefined` | `false` | | Renders the menu panel at mount instead of on the first open (the phone sheet's too), and keeps its rows after a close. Without it the rows mount on each open and go once the menu has closed. |
@@ -2223,9 +2370,13 @@ States are listed in precedence order: when two are on at once and their entries
2223
2370
 
2224
2371
  | State | When it is on |
2225
2372
  | --- | --- |
2373
+ | `activeLink` | Per node, on the entry parts: an entry whose link (`to` / `href`) points at the current page or one of its ancestors — exactly when its `activeClass` is on. The weakest state. |
2374
+ | `exactActiveLink` | Per node, on the entry parts: an entry whose link points at the current page itself — its `exactActiveClass` is on. |
2226
2375
  | `selected` | Per node, on the entry parts: a selectable group's checked entry. |
2227
2376
  | `disabled` | Per node, on the entry parts: an entry that is disabled. |
2228
2377
  | `open` | Component-wide: the menu is open. |
2378
+ | `default` | Per entry, on the entry parts: an entry with `variant: 'default'` or none. Merged after the part's base entry and before its states, so the real states still win. |
2379
+ | `destructive` | Per entry, on the entry parts: `variant: 'destructive'`. Below the real states, like every variant. |
2229
2380
 
2230
2381
  ## Events
2231
2382
 
@@ -2240,12 +2391,12 @@ States are listed in precedence order: when two are on at once and their entries
2240
2391
 
2241
2392
  - `activator` — scope: `BbDropdownActivatorSlotProps` `{ disabled?: boolean; loading: boolean; open: boolean; placement: Placement \| "cursor"; props: BbDropdownActivatorProps }` — The element that triggers the dropdown to open. Spread `props` onto your trigger element.
2242
2393
  - `default` — scope: `any` — Hosts `<BbDropdownGroup>` companions. They register their group with the dropdown and render nothing themselves.
2243
- - `empty` — scope: `BbDropdownPlaceholderSlotProps` `{ groupId?: string; text: string; error?: unknown }` — Replaces the default "no data" placeholder row shown in a pipeline group that resolved to no items. Rendered once per empty group.
2394
+ - `empty` — scope: `BbDropdownPlaceholderSlotProps` `{ groupId?: string; query: string; text: string; error?: unknown }` — Replaces the default "no data" placeholder row shown in a pipeline group that resolved to no items. Rendered once per empty group.
2244
2395
  - `footer` — scope: `BbDropdownEdgeSlotProps` `{ disabled?: boolean; open: boolean; placement: Placement \| "cursor" }` — Content pinned to the bottom of the dropdown panel, outside the scroll area. Stays visible while the items scroll and is separated from them by a divider — for action buttons.
2245
2396
  - `header` — scope: `BbDropdownEdgeSlotProps` `{ disabled?: boolean; open: boolean; placement: Placement \| "cursor" }` — Content pinned to the top of the dropdown panel, outside the scroll area. Stays visible while the items scroll and is separated from them by a divider — for a title or a filter field.
2246
- - `item:append` — scope: `BbDropdownItemSlotProps` `{ description?: string; disabled?: boolean; group: BbDropdownSlotGroup; groupTitle: boolean; level: number; index: number; item: InternalItem; loading: boolean; open: boolean; placement: Placement \| "cursor"; selected: boolean; label: string }` — The trailing region of every row (and group title line), replacing its `append:icon`. A named slot wins over it; the submenu chevron and the check on a selected row take its place.
2247
- - `item:prepend` — scope: `BbDropdownItemSlotProps` `{ description?: string; disabled?: boolean; group: BbDropdownSlotGroup; groupTitle: boolean; level: number; index: number; item: InternalItem; loading: boolean; open: boolean; placement: Placement \| "cursor"; selected: boolean; label: string }` — The leading region of every row (and group title line), replacing its `prepend:icon`. A row's own or its group's named slot wins over it.
2248
- - `loading` — scope: `BbDropdownPlaceholderSlotProps` `{ groupId?: string; text: string; error?: unknown }` — Replaces the default loading placeholder row shown in a pipeline group while its async items load. Rendered once per loading group.
2397
+ - `item:append` — scope: `BbDropdownItemSlotProps` `{ description?: string; disabled?: boolean; group: BbDropdownSlotGroup; groupTitle: boolean; level: number; index: number; item: InternalItem; loading: boolean; open: boolean; placement: Placement \| "cursor"; selected: boolean; label: string; activeLink?: boolean; exactActiveLink?: boolean }` — The trailing region of every row (and group title line), replacing its `append:icon`. A named slot wins over it; the submenu chevron and the check on a selected row take its place.
2398
+ - `item:prepend` — scope: `BbDropdownItemSlotProps` `{ description?: string; disabled?: boolean; group: BbDropdownSlotGroup; groupTitle: boolean; level: number; index: number; item: InternalItem; loading: boolean; open: boolean; placement: Placement \| "cursor"; selected: boolean; label: string; activeLink?: boolean; exactActiveLink?: boolean }` — The leading region of every row (and group title line), replacing its `prepend:icon`. A row's own or its group's named slot wins over it.
2399
+ - `loading` — scope: `BbDropdownPlaceholderSlotProps` `{ groupId?: string; query: string; text: string; error?: unknown }` — Replaces the default loading placeholder row shown in a pipeline group while its async items load. Rendered once per loading group.
2249
2400
  - `#<value>` (named from data) — scope: `BbDropdownItemSlotProps` — One row's content (label and description), by its value; in a fetched group, by its resolved `itemValue`. Reserved names (their slot is one of this component's own, so an item value or column key spelled like them is an error): `activator`, `default`, `empty`, `footer`, `header`, `item`, `loading`.
2250
2401
  - `#<value>:prepend` (named from data) — scope: `BbDropdownItemSlotProps` — One row's leading region; replaces its `prepend:icon`.
2251
2402
  - `#<value>:append` (named from data) — scope: `BbDropdownItemSlotProps` — One row's trailing region; replaces its `append:icon` and its submenu chevron. The check takes its place on the selected row.
@@ -2276,11 +2427,11 @@ Set these on the element, or on a class you put on it, to retune this component
2276
2427
 
2277
2428
  - `BbDropdown` _(this component)_ — its own template binds: `panel` → `CommonPopover`, `root` → `CommonPopover`, `header` → `div.bb-dropdown__header`, `list` → `span.bb-dropdown__list`, `footer` → `div.bb-dropdown__footer`
2278
2429
  - `BbDropdownList` _(internal — not importable, reach it through `BbDropdown`)_ — also mounts `BbIcon`, `ErrorIcon`, `CommonPopover`, `BbDropdownList`
2279
- - `BbBadge` _(public — [contract](./BbBadge.md))_ — its own pt parts (`BbBadge`): `spinner` → `BbSpinner`, `icon` → `BbIcon`, `clear` → `button.bb-badge__clear`; documented CSS variables: `--bg`, `--fg`, `--bw`, `--ring-color`, `--ring-size`, `--min-size-md`, `--min-size`, `--elev`, `--r`, `--fs`, `--icon-size`, `--gap`, `--px`, `--clear-size`, `--bg-disabled`, `--fg-disabled`, `--border-disabled`, `--pad-left`; also mounts `BbIcon`, `BbSpinner`, `BadgeBodyContent`
2430
+ - `BbBadge` _(public — [contract](./BbBadge.md))_ — its own pt parts (`BbBadge`): `spinner` → `BbSpinner`, `icon` → `span.bb-badge__append-icon`, `icon` → `BbIcon`, `clear` → `button.bb-badge__clear`; documented CSS variables: `--bg`, `--fg`, `--bw`, `--ring-color`, `--ring-size`, `--min-size-md`, `--min-size`, `--elev`, `--r`, `--fs`, `--icon-size`, `--gap`, `--px`, `--clear-size`, `--bg-disabled`, `--fg-disabled`, `--border-disabled`, `--pad-left`; also mounts `BbIcon`, `BbSpinner`, `BadgeBodyContent`
2280
2431
  - `AdaptiveDropdown` _(internal — not importable, reach it through `BbDropdown`)_ — hands `BbOffCanvas` the pt map `{ header: 'header', footer: 'footer', panel: 'root', sheet: 'root' }` (ours → theirs); also mounts `BbSmoothHeight`, `BbDropdownList`
2281
2432
  - `BbOffCanvas` _(public — [contract](./BbOffCanvas.md))_ — its own pt parts (`BbOffCanvas`): `header` → `div.bb-offcanvas__header`, `title` → `span.bb-offcanvas__title`, `description` → `p.bb-offcanvas__description`, `close` → `CloseButton`, `content` → `div.bb-offcanvas__content`, `footer` → `div.bb-offcanvas__footer`; documented CSS variables: `--stack-x`, `--snap-y`, `--stack-fill`, `--stack-transition-duration`, `--px`, `--handle-w`, `--handle-h`, `--handle-bg`, `--safe-top`, `--close-size`
2282
2433
  - `CloseButton` _(internal — not importable, reach it through `BbOffCanvas`)_ — documented CSS variables: `--size`, `--p`
2283
- - `BbButton` _(public — [contract](./BbButton.md))_ — its own pt parts (`BbButton`): `root` → `BbBaseButton`, `spinner` → `BbSpinner`, `icon` → `BbIcon`, `text` → `span.bb-button__text`; documented CSS variables: `--h-xs`, `--h`, `--icon-size`, `--px`, `--fs`, `--r`, `--gap`, `--bg`, `--bg-hover`, `--bg-pressed`, `--fg`, `--border-color`, `--ring-color`, `--ring-size`, `--bw`, `--fg-hover`, `--border-hover`, `--fg-pressed`, `--border-pressed`, `--bg-disabled`, `--fg-disabled`, `--border-disabled`, `--opacity-disabled`, `--elev-tier`, `--elev`; also mounts `BbIcon`, `BbSpinner`
2434
+ - `BbButton` _(public — [contract](./BbButton.md))_ — its own pt parts (`BbButton`): `root` → `BbBaseButton`, `spinner` → `BbSpinner`, `icon` → `span.bb-button__icon`, `icon` → `BbIcon`, `icon` → `span.bb-button__prepend-icon`, `text` → `span.bb-button__text`, `icon` → `span.bb-button__append-icon`; documented CSS variables: `--h-xs`, `--h`, `--icon-size`, `--px`, `--fs`, `--r`, `--gap`, `--bg`, `--bg-hover`, `--bg-pressed`, `--fg`, `--border-color`, `--ring-color`, `--ring-size`, `--bw`, `--fg-hover`, `--border-hover`, `--fg-pressed`, `--border-pressed`, `--bg-disabled`, `--fg-disabled`, `--border-disabled`, `--opacity-disabled`, `--elev-tier`, `--elev`; also mounts `BbIcon`, `BbSpinner`
2284
2435
  - `BbBaseButton` _(public — [contract](./BbBaseButton.md))_ — its own pt parts (`BbBaseButton`): `root` → `RouterComponent`, `root` → `a`, `root` → `component`; also mounts `RouterComponent`
2285
2436
  - Also mounts `CommonPopover`, `DropdownPipelineResolver`
2286
2437
 
@@ -379,6 +379,13 @@ activator slot: the toggle button is part of the component's contract.
379
379
  </BbDropdownButton>
380
380
  ```
381
381
 
382
+ ### Icons on the main button
383
+
384
+ `icon`, `prepend:icon` and `append:icon` belong to the main button, as on
385
+ `BbButton`, and so do the slots of the same names: `#prepend:icon="{ loading,
386
+ activeLink }"` swaps its glyph with its states, inside its `icon` part. The
387
+ toggle keeps its chevron (`toggle:icon` above).
388
+
382
389
  ### Selectable groups in a split button
383
390
 
384
391
  Because it shares `BbDropdown`'s items, a split button can also host a
@@ -637,7 +644,7 @@ A `pt` value written in `<script>` (a constant, a preset, a wrapper's
637
644
 
638
645
  | Prop | Type | Default | Required | Description |
639
646
  | --- | --- | --- | --- | --- |
640
- | `pt:<part>`, `pt:<part>:<state>`, `pt` | `PtValue` | | | Passthrough — a class list, or `{ class, style, attrs }`, bound to one named part of the component; the state form applies only while that state is on. Parts: `footer`, `header`, `icon`, `input`, `item`, `itemDescription`, `itemIcon`, `itemText`, `main`, `panel`, `root`, `sheet`, `spinner`, `text`, `toggle`. States: `disabled`, `loading`, `open`, `selected`. What each one is: _Passthrough parts and states_ below. `pt` is the object form with the same keys minus the prefix (`{ icon: '…', 'icon:loading': '…' }`). See `guides/passthrough.md`. In `<script>` a `pt` value is typed `BbDropdownButtonProps['pt']` (`PtValue` is not exported): § Typing `pt` in `<script>`. |
647
+ | `pt:<part>`, `pt:<part>:<state>`, `pt` | `PtValue` | | | Passthrough — a class list, or `{ class, style, attrs }`, bound to one named part of the component; the state form applies only while that state is on. Parts: `footer`, `header`, `icon`, `input`, `item`, `itemDescription`, `itemIcon`, `itemText`, `main`, `panel`, `root`, `sheet`, `spinner`, `text`, `toggle`. States: `activeLink`, `disabled`, `exactActiveLink`, `loading`, `open`, `destructive`, `ghost`, `link`, `outline`, `primary`, `secondary`, `default`, `selected`. What each one is: _Passthrough parts and states_ below. `pt` is the object form with the same keys minus the prefix (`{ icon: '…', 'icon:loading': '…' }`). See `guides/passthrough.md`. In `<script>` a `pt` value is typed `BbDropdownButtonProps['pt']` (`PtValue` is not exported): § Typing `pt` in `<script>`. |
641
648
  | `activeClass` | `string \| undefined` | | | Class to apply when the link is active. |
642
649
  | `adaptive` | `boolean \| undefined` | ``config.adaptive` (`true`)` | | On mobile (below `config.mobileMaxWidth`), renders the menu as a bottom sheet that drills into submenus one level at a time instead of hover flyouts; slots and selection are unchanged. Unset: the global `config.adaptive`. |
643
650
  | `append:icon` | `string \| undefined` | | | Icon to be added on the right of the text. |
@@ -646,6 +653,7 @@ A `pt` value written in `<script>` (a constant, a preset, a wrapper's
646
653
  | `block` | `boolean \| undefined` | `false` | | Displays the component as full width. |
647
654
  | `cacheFor` | `string \| number \| (string \| number)[] \| undefined` | | | Inertia: how long a prefetched response stays fresh before it is re-fetched. A single duration, or `[staleAfter, expiresAfter]`. Typed platform-agnostically (matching BbButton) so the library's types never require `@inertiajs/vue3` to be installed. |
648
655
  | `cacheTags` | `string \| string[] \| undefined` | | | Inertia: tags to file this visit's prefetch cache under, so a later request can invalidate the whole tagged group. |
656
+ | `closeOnSelect` | `boolean \| undefined` | `unset (close after a single-choice change)` | | Whether a selection closes the menu. Unset: a change in a single-choice group closes it (an untick too, at any depth); a `multiple` group stays open. `true`: every selection change closes it. `false`: none does. |
649
657
  | `compact` | `boolean \| undefined` | `false` | | Compact density, the `BbSelect` ladder: flyout rows 28px → 24px, sheet rows 44px → 36px (a sheet row stays a touch target). The label size does not change. |
650
658
  | `component` | `string \| undefined` | | | Inertia: the page component this visit resolves to. Rarely set by hand — the server normally decides it. |
651
659
  | `data` | `object \| undefined` | | | Request payload forwarded to Inertia when navigating via `href` in an Inertia-enabled app. Ignored when not using Inertia. |
@@ -727,10 +735,19 @@ States are listed in precedence order: when two are on at once and their entries
727
735
 
728
736
  | State | When it is on |
729
737
  | --- | --- |
738
+ | `activeLink` | Component-wide while the main button is a link to the current page or one of its ancestors (its `activeClass` is on); also per node, on the entry parts: a menu entry whose link is. The weakest state. |
739
+ | `exactActiveLink` | Component-wide while the main button is a link to the current page itself (its `exactActiveClass` is on); also per node, on the entry parts. |
730
740
  | `selected` | Per node, on the entry parts only (`item`, `itemText`, `itemIcon`, `itemDescription`): a selectable group's checked entry. `pt:root:selected` is a type error, not a silent no-op. |
731
741
  | `disabled` | Component-wide while the control is `disabled`; also per node, on the entry parts: a menu entry that is disabled. |
732
742
  | `open` | Component-wide: the menu is open. |
733
743
  | `loading` | Component-wide: `loading` is on — the main button shows its spinner. |
744
+ | `destructive` | Per entry, on the entry parts: `variant: 'destructive'`. Below the real states, like every variant. |
745
+ | `ghost` | Component-wide: `variant="ghost"`. Below the real states, like every variant. |
746
+ | `link` | Component-wide: `variant="link"`. Below the real states, like every variant. |
747
+ | `outline` | Component-wide: `variant="outline"`. Below the real states, like every variant. |
748
+ | `primary` | Component-wide: `variant="primary"`. Merged after the part's base entry and before its states, so `disabled` / `loading` still win. |
749
+ | `secondary` | Component-wide: `variant="secondary"`. Below the real states, like every variant. |
750
+ | `default` | Per entry, on the entry parts: an entry with `variant: 'default'` or none. Merged after the part's base entry and before its states, so the real states still win. |
734
751
 
735
752
  ## Events
736
753
 
@@ -743,21 +760,24 @@ States are listed in precedence order: when two are on at once and their entries
743
760
 
744
761
  ## Slots
745
762
 
763
+ - `append:icon` — scope: `BbButtonIconSlotProps` `{ activeLink: boolean; exactActiveLink: boolean; pressed: boolean; loading: boolean; disabled: boolean }` — The main button's trailing icon, replacing `append:icon`.
746
764
  - `default` — scope: `any` — The main button's label (falls back to `text`).
747
765
  - `dropdown` — scope: `any` — Menu companions, forwarded into the dropdown: a `<BbDropdownGroup>` binding a group of `items` by id. A `#dropdown` slot keeps the toggle even with no `items`.
748
- - `empty` — scope: `BbDropdownPlaceholderSlotProps` `{ groupId?: string; text: string; error?: unknown }` — Replaces the default "no data" placeholder row shown in a pipeline group that resolved to no items. Rendered once per empty group. Forwarded to the underlying `BbDropdown`.
766
+ - `empty` — scope: `BbDropdownPlaceholderSlotProps` `{ groupId?: string; query: string; text: string; error?: unknown }` — Replaces the default "no data" placeholder row shown in a pipeline group that resolved to no items. Rendered once per empty group. Forwarded to the underlying `BbDropdown`.
749
767
  - `footer` — scope: `BbDropdownButtonEdgeSlotProps` `{ disabled?: boolean; open: boolean; placement: Placement \| "cursor" }` — Content pinned to the bottom of the dropdown panel, outside the scroll area.
750
768
  - `header` — scope: `BbDropdownButtonEdgeSlotProps` `{ disabled?: boolean; open: boolean; placement: Placement \| "cursor" }` — Content pinned to the top of the dropdown panel, outside the scroll area.
769
+ - `icon` — scope: `BbButtonIconSlotProps` `{ activeLink: boolean; exactActiveLink: boolean; pressed: boolean; loading: boolean; disabled: boolean }` — The main button's icon, replacing the `icon` prop; inside its `icon` part (`pt:icon`, `--icon-size`, the loading spinner), with its states.
751
770
  - `item:append` — scope: `BbDropdownButtonItemSlotProps` `{ disabled?: boolean; index: number; item: InternalItem; open: boolean; placement: Placement \| "cursor"; label: string }` — The trailing region of every menu row, replacing its `append:icon`; the submenu chevron and the check on a selected row take its place (BbDropdown's rules).
752
771
  - `item:prepend` — scope: `BbDropdownButtonItemSlotProps` `{ disabled?: boolean; index: number; item: InternalItem; open: boolean; placement: Placement \| "cursor"; label: string }` — The leading region of every menu row, replacing its `prepend:icon` (BbDropdown's rules).
753
- - `loading` — scope: `BbDropdownPlaceholderSlotProps` `{ groupId?: string; text: string; error?: unknown }` — Replaces the default loading placeholder row shown in a pipeline group while its async items load. Rendered once per loading group. Forwarded to the underlying `BbDropdown`.
754
- - `#<value>` (named from data) — scope: `BbDropdownItemSlotProps` — One row's content (label and description), by its value; in a fetched group, by its resolved `itemValue`. Reserved names (their slot is one of this component's own, so an item value or column key spelled like them is an error): `default`, `dropdown`, `empty`, `footer`, `header`, `item`, `loading`.
772
+ - `loading` — scope: `BbDropdownPlaceholderSlotProps` `{ groupId?: string; query: string; text: string; error?: unknown }` — Replaces the default loading placeholder row shown in a pipeline group while its async items load. Rendered once per loading group. Forwarded to the underlying `BbDropdown`.
773
+ - `prepend:icon` — scope: `BbButtonIconSlotProps` `{ activeLink: boolean; exactActiveLink: boolean; pressed: boolean; loading: boolean; disabled: boolean }` — The main button's leading icon, replacing `prepend:icon`.
774
+ - `#<value>` (named from data) — scope: `BbDropdownItemSlotProps` — One row's content (label and description), by its value; in a fetched group, by its resolved `itemValue`. Reserved names (their slot is one of this component's own, so an item value or column key spelled like them is an error): `default`, `dropdown`, `empty`, `footer`, `header`, `icon`, `item`, `loading`.
755
775
  - `#<value>:prepend` (named from data) — scope: `BbDropdownItemSlotProps` — One row's leading region; replaces its `prepend:icon`.
756
776
  - `#<value>:append` (named from data) — scope: `BbDropdownItemSlotProps` — One row's trailing region; replaces its `append:icon` and its submenu chevron. The check takes its place on the selected row.
757
777
  - `#<groupId>:item` (named from data) — scope: `BbDropdownItemSlotProps` — The content of every row of that group, and no other; a row's own `#<value>` still wins.
758
778
  - `#<groupId>:item:prepend` (named from data) — scope: `BbDropdownItemSlotProps` — The leading region of every row of that group — e.g. an avatar per record, drawn from `item.meta`, while other rows keep their `prepend:icon`.
759
779
  - `#<groupId>:item:append` (named from data) — scope: `BbDropdownItemSlotProps` — The trailing region of every row of that group; replaces their `append:icon`. The check takes its place on the selected row.
760
- - `#<groupId>` (named from data) — scope: `BbDropdownItemSlotProps` — That group's title line (the group is passed as `item`, `groupTitle` is `true`). Reserved names (their slot is one of this component's own, so an item value or column key spelled like them is an error): `default`, `dropdown`, `empty`, `footer`, `header`, `item`, `loading`.
780
+ - `#<groupId>` (named from data) — scope: `BbDropdownItemSlotProps` — That group's title line (the group is passed as `item`, `groupTitle` is `true`). Reserved names (their slot is one of this component's own, so an item value or column key spelled like them is an error): `default`, `dropdown`, `empty`, `footer`, `header`, `icon`, `item`, `loading`.
761
781
  - `#<groupId>:prepend` (named from data) — scope: `BbDropdownItemSlotProps` — The leading region of that group's title line.
762
782
  - `#<groupId>:append` (named from data) — scope: `BbDropdownItemSlotProps` — The trailing region of that group's title line.
763
783
 
@@ -771,11 +791,11 @@ States are listed in precedence order: when two are on at once and their entries
771
791
  - Listed nodes are the ones `BbDropdownButton`'s API reaches or that declare a documented CSS variable; the rest fold into "also mounts". The complete tree, repeats and undocumented locals included, is `manifest/components/BbDropdownButton.tree.json`.
772
792
 
773
793
  - `BbDropdownButton` _(this component)_ — hands `BbButton` the pt map `{ main: 'root', icon: 'icon', spinner: 'spinner', text: 'text' }` (ours → theirs); hands `BbDropdown` the pt map `{ panel: 'panel', sheet: 'sheet', header: 'header', footer: 'footer', input: 'input', item: 'item', itemText: 'itemText', itemIcon: 'itemIcon', itemDescription: 'itemDescription' }` (ours → theirs); hands `BbButton` the pt map `{ toggle: 'root' }` (ours → theirs)
774
- - `BbButton` _(public — [contract](./BbButton.md))_ — its own pt parts (`BbButton`): `root` → `BbBaseButton`, `spinner` → `BbSpinner`, `icon` → `BbIcon`, `text` → `span.bb-button__text`; documented CSS variables: `--h-xs`, `--h`, `--icon-size`, `--px`, `--fs`, `--r`, `--gap`, `--bg`, `--bg-hover`, `--bg-pressed`, `--fg`, `--border-color`, `--ring-color`, `--ring-size`, `--bw`, `--fg-hover`, `--border-hover`, `--fg-pressed`, `--border-pressed`, `--bg-disabled`, `--fg-disabled`, `--border-disabled`, `--opacity-disabled`, `--elev-tier`, `--elev`; also mounts `BbIcon`, `BbSpinner`
794
+ - `BbButton` _(public — [contract](./BbButton.md))_ — its own pt parts (`BbButton`): `root` → `BbBaseButton`, `spinner` → `BbSpinner`, `icon` → `span.bb-button__icon`, `icon` → `BbIcon`, `icon` → `span.bb-button__prepend-icon`, `text` → `span.bb-button__text`, `icon` → `span.bb-button__append-icon`; documented CSS variables: `--h-xs`, `--h`, `--icon-size`, `--px`, `--fs`, `--r`, `--gap`, `--bg`, `--bg-hover`, `--bg-pressed`, `--fg`, `--border-color`, `--ring-color`, `--ring-size`, `--bw`, `--fg-hover`, `--border-hover`, `--fg-pressed`, `--border-pressed`, `--bg-disabled`, `--fg-disabled`, `--border-disabled`, `--opacity-disabled`, `--elev-tier`, `--elev`; also mounts `BbIcon`, `BbSpinner`
775
795
  - `BbBaseButton` _(public — [contract](./BbBaseButton.md))_ — its own pt parts (`BbBaseButton`): `root` → `RouterComponent`, `root` → `a`, `root` → `component`; also mounts `RouterComponent`
776
796
  - `BbDropdown` _(public — [contract](./BbDropdown.md))_ — its own pt parts (`BbDropdown`): `panel` → `CommonPopover`, `root` → `CommonPopover`, `header` → `div.bb-dropdown__header`, `list` → `span.bb-dropdown__list`, `footer` → `div.bb-dropdown__footer`; documented CSS variables: `--menu-inset`; also mounts `CommonPopover`, `DropdownPipelineResolver`
777
797
  - `BbDropdownList` _(internal — not importable, reach it through `BbDropdown`)_ — also mounts `BbIcon`, `ErrorIcon`, `CommonPopover`, `BbDropdownList`
778
- - `BbBadge` _(public — [contract](./BbBadge.md))_ — its own pt parts (`BbBadge`): `spinner` → `BbSpinner`, `icon` → `BbIcon`, `clear` → `button.bb-badge__clear`; documented CSS variables: `--bg`, `--fg`, `--bw`, `--ring-color`, `--ring-size`, `--min-size-md`, `--min-size`, `--elev`, `--r`, `--fs`, `--icon-size`, `--gap`, `--px`, `--clear-size`, `--bg-disabled`, `--fg-disabled`, `--border-disabled`, `--pad-left`; also mounts `BbIcon`, `BbSpinner`, `BadgeBodyContent`
798
+ - `BbBadge` _(public — [contract](./BbBadge.md))_ — its own pt parts (`BbBadge`): `spinner` → `BbSpinner`, `icon` → `span.bb-badge__append-icon`, `icon` → `BbIcon`, `clear` → `button.bb-badge__clear`; documented CSS variables: `--bg`, `--fg`, `--bw`, `--ring-color`, `--ring-size`, `--min-size-md`, `--min-size`, `--elev`, `--r`, `--fs`, `--icon-size`, `--gap`, `--px`, `--clear-size`, `--bg-disabled`, `--fg-disabled`, `--border-disabled`, `--pad-left`; also mounts `BbIcon`, `BbSpinner`, `BadgeBodyContent`
779
799
  - `AdaptiveDropdown` _(internal — not importable, reach it through `BbDropdown`)_ — hands `BbOffCanvas` the pt map `{ header: 'header', footer: 'footer', panel: 'root', sheet: 'root' }` (ours → theirs); also mounts `BbSmoothHeight`, `BbButton`, `BbDropdownList`
780
800
  - `BbOffCanvas` _(public — [contract](./BbOffCanvas.md))_ — its own pt parts (`BbOffCanvas`): `header` → `div.bb-offcanvas__header`, `title` → `span.bb-offcanvas__title`, `description` → `p.bb-offcanvas__description`, `close` → `CloseButton`, `content` → `div.bb-offcanvas__content`, `footer` → `div.bb-offcanvas__footer`; documented CSS variables: `--stack-x`, `--snap-y`, `--stack-fill`, `--stack-transition-duration`, `--px`, `--handle-w`, `--handle-h`, `--handle-bg`, `--safe-top`, `--close-size`
781
801
  - `CloseButton` _(internal — not importable, reach it through `BbOffCanvas`)_ — documented CSS variables: `--size`, `--p`
package/dist/ai/BbForm.md CHANGED
@@ -507,14 +507,16 @@ on the problem instead of hunting for it. A field inside a `v-show`-hidden
507
507
  section, an `inert` subtree or `hidden` content is skipped; a failing path with
508
508
  no field mounted has nothing to focus.
509
509
 
510
- It also emits **`invalid`** with `{ errors, values }`: `errors` maps every
511
- failing path to all its messages, `{ [path]: string[] }` — the same shape and
512
- keys as the form's `errors` (`address.city`, `lines.0.qty`) — including the
513
- paths that are not on screen. That is what a wizard needs to jump to the
514
- step holding the error; focus runs after your handler, so the field that just
515
- came back on screen is the one that gets it. A handler that moves focus itself
516
- (to an [error summary](#a-summary-of-the-problems-at-the-top-of-a-long-form),
517
- say) keeps it: the form leaves focus alone then.
510
+ It also emits **`invalid`** with `{ errors, values, unmatched, source }`:
511
+ `errors` maps every failing path to all its messages, `{ [path]: string[] }` —
512
+ the same shape and keys as the form's `errors` (`address.city`, `lines.0.qty`)
513
+ — including the paths that are not on screen. That is what a wizard needs to
514
+ jump to the step holding the error; focus runs after your handler, so the
515
+ field that just came back on screen is the one that gets it. A handler that
516
+ moves focus itself (to an [error summary](#a-summary-of-the-problems-at-the-top-of-a-long-form),
517
+ say) keeps it: the form leaves focus alone then. `unmatched` holds the paths
518
+ no field claims (the same shape as `unmatchedErrors`), `{}` when every error
519
+ has its field.
518
520
 
519
521
  ```vue
520
522
  <BbForm
@@ -529,13 +531,38 @@ say) keeps it: the form leaves focus alone then.
529
531
  > Behaviour change in 3.0.0-beta.41: a failed submit used to leave focus where
530
532
  > it was (usually on the submit button).
531
533
 
532
- **Server errors move focus the same way.** `setErrors(map)` focuses the first
533
- field that got an error and is on screen, in page order (not the first key of
534
- the map). It holds back when focus is no longer the form's: if the user has
535
- moved on to something outside the form while the request ran, or if nobody
536
- has submitted the form yet and focus is on the page (a page that opens with
537
- errors in its props). Keys that match no field, and an empty map, move
538
- nothing; `setErrors(map, { focus: false })` never moves it.
534
+ **Server errors move focus the same way, and emit the same event.**
535
+ `setErrors(map)` focuses the first field that got an error and is on screen,
536
+ in page order (not the first key of the map). It holds back when focus is no
537
+ longer the form's: if the user has moved on to something outside the form
538
+ while the request ran, or if nobody has submitted the form yet and focus is
539
+ on the page (a page that opens with errors in its props). Keys that match no
540
+ field, and an empty map, move nothing; `setErrors(map, { focus: false })`
541
+ never moves it. Either way a non-empty map emits `invalid` first, with
542
+ `source: 'server'`, and so does a [`field-errors`](#the-pages-errors-object-field-errors)
543
+ value that brings messages. Errors a page loads with emit nothing: nobody
544
+ submitted. One `@invalid` handler then covers both answers (`source:
545
+ 'submit'` for a failed submit).
546
+
547
+ Errors no field shows (a 422 keyed on `assignments.0.id`, drawn by hand in
548
+ its row) get no focus from the form: bring their row on screen in the
549
+ handler, and the form leaves focus where you put it.
550
+
551
+ ```ts
552
+ import type { BbFormInvalidPayload } from 'bitboss-ui';
553
+
554
+ const onInvalid = ({ unmatched }: BbFormInvalidPayload) => {
555
+ const key = Object.keys(unmatched).find((path) =>
556
+ path.startsWith('assignments.')
557
+ );
558
+ if (!key) return;
559
+ const row = document.querySelector<HTMLElement>(
560
+ `[data-row="${key.split('.')[1]}"]`
561
+ );
562
+ row?.scrollIntoView({ block: 'center' });
563
+ row?.focus({ preventScroll: true }); // a row with tabindex="-1"
564
+ };
565
+ ```
539
566
 
540
567
  ### A summary of the problems at the top of a long form
541
568
 
@@ -752,10 +779,10 @@ How it works:
752
779
  empties on a successful one, never while the user types: fixing one field
753
780
  does not reshuffle the box above them. The messages under the fields
754
781
  update as usual.
755
- - **Server errors** (`setErrors` after a 422) do not emit `invalid`. To list
756
- them too, build the same list from
757
- `{ ...form.errors.value, ...form.unmatchedErrors.value }` right after the
758
- call. A path no field has (in `unmatchedErrors`) is an item without a link.
782
+ - **Server errors list the same way.** A 422 through `setErrors` or
783
+ `field-errors` emits `invalid` too (`source: 'server'`), so the same handler
784
+ builds the box from the server's answer. A path no field has (in
785
+ `unmatched`) is an item with no field to focus.
759
786
 
760
787
  **When not to use it: a short form**, where every field is on screen. A
761
788
  failed submit already focuses the first invalid field and every message sits
@@ -1357,6 +1384,11 @@ async function save(values: Record<string, unknown>) {
1357
1384
  }
1358
1385
  ```
1359
1386
 
1387
+ A value that brings a message the object did not have (or comes back after a
1388
+ new submit) is an answer: it emits `invalid` with `source: 'server'` and
1389
+ moves focus like `setErrors`. The same content passed again, a message
1390
+ leaving, and the object the page loads with do neither.
1391
+
1360
1392
  ##### A message of your own that comes and goes
1361
1393
 
1362
1394
  The object does not have to come from a server. A check that spans fields is
@@ -1514,8 +1546,9 @@ const save = async (values: Record<string, unknown>) => {
1514
1546
  instead: it shows while its key holds a message and goes the moment it
1515
1547
  does not. Calling `validate()` to wipe a `setErrors` message runs every
1516
1548
  rule on every field, so it puts messages on fields the user never touched.
1517
- - `setErrors` does not emit `invalid`. It does move focus to the first failing
1518
- field on screen, like a failed submit (`{ focus: false }` opts out; see
1549
+ - `setErrors` emits `invalid` (`source: 'server'`) for a non-empty map, then
1550
+ moves focus to the first failing field on screen, like a failed submit
1551
+ (`{ focus: false }` opts out of the focus, not the event; see
1519
1552
  [A failed submit focuses the first invalid field](#a-failed-submit-focuses-the-first-invalid-field)).
1520
1553
 
1521
1554
  #### Fields on a step you left, and the step badge
@@ -1833,7 +1866,7 @@ marker stays consistent with the rules.
1833
1866
 
1834
1867
  ## Events
1835
1868
 
1836
- - `invalid` — `(payload: BbFormInvalidPayload): void` — Fired when a submit fails validation, after the form has focused the first invalid field that is on screen. `errors` also holds paths with no field showing, so a wizard can jump to the step that has one.
1869
+ - `invalid` — `(payload: BbFormInvalidPayload): void` — Fired when a submit fails validation (`source: 'submit'`) or server errors land (`'server'`: `setErrors()`, a new `field-errors` message), before the form's own focus; a handler that moves focus keeps it. `unmatched`: the paths no field claims.
1837
1870
  - `submit` — `(values: BbFormSubmitValues<S>, meta: BbFormSubmitMeta): void` — Fired only when every field passes. Carries the values — with a schema, its parsed OUTPUT (transforms and coercions applied), typed from it.
1838
1871
 
1839
1872
  ## Slots