bitboss-ui 3.0.0-beta.2 → 3.0.0-beta.21

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 (350) hide show
  1. package/README.md +72 -33
  2. package/dist/ai/BbAccordion.md +3 -3
  3. package/dist/ai/BbAlert.md +9 -5
  4. package/dist/ai/BbAsterisk.md +17 -0
  5. package/dist/ai/BbAvatar.md +12 -3
  6. package/dist/ai/BbBadge.md +15 -10
  7. package/dist/ai/BbBadgeButton.md +16 -16
  8. package/dist/ai/BbBaseButton.md +16 -16
  9. package/dist/ai/BbBaseCheckbox.md +8 -8
  10. package/dist/ai/BbBaseCheckboxGroup.md +13 -13
  11. package/dist/ai/BbBaseCheckboxIcon.md +26 -9
  12. package/dist/ai/BbBaseColorInput.md +11 -11
  13. package/dist/ai/BbBaseDatePicker.md +6 -6
  14. package/dist/ai/BbBaseDatePickerInput.md +15 -15
  15. package/dist/ai/BbBaseInputContainer.md +12 -12
  16. package/dist/ai/BbBaseNumberInput.md +8 -8
  17. package/dist/ai/BbBaseRadio.md +5 -5
  18. package/dist/ai/BbBaseRadioGroup.md +7 -7
  19. package/dist/ai/BbBaseRadioIcon.md +25 -9
  20. package/dist/ai/BbBaseRating.md +5 -5
  21. package/dist/ai/BbBaseSelect.md +22 -21
  22. package/dist/ai/BbBaseSlider.md +9 -9
  23. package/dist/ai/BbBaseSwitch.md +8 -8
  24. package/dist/ai/BbBaseSwitchGroup.md +13 -13
  25. package/dist/ai/BbBaseSwitchIcon.md +28 -10
  26. package/dist/ai/BbBaseTag.md +10 -10
  27. package/dist/ai/BbBaseTextInput.md +9 -9
  28. package/dist/ai/BbBaseTextarea.md +9 -9
  29. package/dist/ai/BbBaseTimePickerInput.md +13 -13
  30. package/dist/ai/BbBreadcrumbs.md +2 -2
  31. package/dist/ai/BbButton.md +15 -15
  32. package/dist/ai/BbCheckbox.md +18 -17
  33. package/dist/ai/BbCheckboxGroup.md +15 -14
  34. package/dist/ai/BbCollapsible.md +11 -9
  35. package/dist/ai/BbColorInput.md +15 -15
  36. package/dist/ai/BbColorPalette.md +12 -6
  37. package/dist/ai/BbConfirm.md +12 -5
  38. package/dist/ai/BbDatePicker.md +13 -13
  39. package/dist/ai/BbDatePickerInput.md +20 -20
  40. package/dist/ai/BbDialog.md +78 -12
  41. package/dist/ai/BbDropdown.md +132 -22
  42. package/dist/ai/BbDropdownButton.md +12 -12
  43. package/dist/ai/BbDropdownGroup.md +1 -1
  44. package/dist/ai/BbDropzone.md +4 -4
  45. package/dist/ai/BbForm.md +49 -14
  46. package/dist/ai/BbIcon.md +6 -0
  47. package/dist/ai/BbIndicator.md +5 -5
  48. package/dist/ai/BbNumberInput.md +11 -11
  49. package/dist/ai/BbOffCanvas.md +90 -20
  50. package/dist/ai/BbPagination.md +3 -3
  51. package/dist/ai/BbPopover.md +32 -17
  52. package/dist/ai/BbProgress.md +7 -4
  53. package/dist/ai/BbRadio.md +16 -15
  54. package/dist/ai/BbRadioGroup.md +20 -17
  55. package/dist/ai/BbRating.md +11 -11
  56. package/dist/ai/BbSelect.md +138 -37
  57. package/dist/ai/BbSelectPopover.md +40 -41
  58. package/dist/ai/BbSlider.md +37 -22
  59. package/dist/ai/BbSmoothHeight.md +1 -1
  60. package/dist/ai/BbSwitch.md +25 -10
  61. package/dist/ai/BbSwitchGroup.md +17 -14
  62. package/dist/ai/BbTable.md +920 -109
  63. package/dist/ai/BbTabs.md +28 -14
  64. package/dist/ai/BbTabsList.md +1 -1
  65. package/dist/ai/BbTabsPanes.md +1 -1
  66. package/dist/ai/BbTabsRoot.md +10 -10
  67. package/dist/ai/BbTag.md +14 -14
  68. package/dist/ai/BbTextInput.md +16 -15
  69. package/dist/ai/BbTextarea.md +13 -13
  70. package/dist/ai/BbTimePicker.md +10 -10
  71. package/dist/ai/BbTimePickerInput.md +17 -17
  72. package/dist/ai/BbToast.md +1 -1
  73. package/dist/ai/BbToastPortal.md +1 -1
  74. package/dist/ai/BbTooltip.md +12 -10
  75. package/dist/ai/BbTree.md +1 -1
  76. package/dist/ai/ChipsBox.md +1 -1
  77. package/dist/ai/CommonInputWrapper.md +10 -10
  78. package/dist/ai/CommonPopover.md +6 -6
  79. package/dist/ai/FlatListBox.md +5 -5
  80. package/dist/ai/GroupedListBox.md +5 -5
  81. package/dist/ai/ListBox.md +4 -4
  82. package/dist/ai/OptionsContainer.md +4 -4
  83. package/dist/ai/changelog.json +128 -2
  84. package/dist/ai/components.json +917 -100
  85. package/dist/ai/composables/useBbConfig.md +72 -11
  86. package/dist/ai/composables/useConfirm.md +5 -3
  87. package/dist/ai/guides/ai-router.md +9 -4
  88. package/dist/ai/guides/component-picker.md +18 -18
  89. package/dist/ai/guides/design-language.md +14 -14
  90. package/dist/ai/guides/design-tokens.md +25 -18
  91. package/dist/ai/guides/fetch-items-playbook.md +1 -1
  92. package/dist/ai/guides/icons-policy.md +11 -5
  93. package/dist/ai/guides/installation-and-plugin-setup.md +85 -23
  94. package/dist/ai/guides/migration/components/bb-checkbox-group.md +30 -0
  95. package/dist/ai/guides/migration/components/bb-checkbox.md +37 -3
  96. package/dist/ai/guides/migration/components/bb-collapsible.md +5 -5
  97. package/dist/ai/guides/migration/components/bb-dialog.md +62 -34
  98. package/dist/ai/guides/migration/components/bb-offcanvas.md +29 -13
  99. package/dist/ai/guides/migration/components/bb-rating.md +8 -2
  100. package/dist/ai/guides/migration/components/bb-select.md +5 -2
  101. package/dist/ai/guides/migration/components/bb-table.md +560 -28
  102. package/dist/ai/guides/migration/components/bb-text-input.md +6 -6
  103. package/dist/ai/guides/migration/v2-to-v3.md +260 -89
  104. package/dist/ai/guides/ssr-prefill.md +120 -0
  105. package/dist/ai/index.md +10 -9
  106. package/dist/ai/recipes/inertia/approvals-inbox.md +0 -1
  107. package/dist/ai/recipes/inertia/inline-edit-workspace.md +0 -1
  108. package/dist/ai/recipes/inertia/layout-scaffold.md +5 -1
  109. package/dist/ai/recipes/inertia/ownership-atlas.md +49 -29
  110. package/dist/ai/recipes/inertia/record-form.md +0 -1
  111. package/dist/ai/recipes/inertia/records-workspace.md +26 -8
  112. package/dist/ai/recipes/inertia/upload-center.md +0 -1
  113. package/dist/ai/recipes/nuxt/approvals-inbox.md +0 -1
  114. package/dist/ai/recipes/nuxt/inline-edit-workspace.md +0 -1
  115. package/dist/ai/recipes/nuxt/layout-scaffold.md +5 -1
  116. package/dist/ai/recipes/nuxt/record-form.md +0 -1
  117. package/dist/ai/recipes/nuxt/records-workspace.md +26 -8
  118. package/dist/ai/recipes/nuxt/upload-center.md +0 -1
  119. package/dist/ai/recipes/vue/approvals-inbox.md +0 -1
  120. package/dist/ai/recipes/vue/inline-edit-workspace.md +0 -1
  121. package/dist/ai/recipes/vue/layout-scaffold.md +5 -1
  122. package/dist/ai/recipes/vue/records-workspace.md +26 -8
  123. package/dist/ai/recipes/vue/upload-center.md +0 -1
  124. package/dist/ai/source/BbAlert.md +60 -2
  125. package/dist/ai/source/BbAvatar.md +3 -3
  126. package/dist/ai/source/BbBadge.md +60 -15
  127. package/dist/ai/source/BbBadgeButton.md +8 -2
  128. package/dist/ai/source/BbBaseButton.md +3 -1
  129. package/dist/ai/source/BbBaseCheckboxGroup.md +6 -2
  130. package/dist/ai/source/BbBaseDatePicker.md +3 -1
  131. package/dist/ai/source/BbBaseDatePickerInput.md +5 -1
  132. package/dist/ai/source/BbBaseInputContainer.md +52 -1
  133. package/dist/ai/source/BbBaseRadioGroup.md +4 -0
  134. package/dist/ai/source/BbBaseRating.md +3 -1
  135. package/dist/ai/source/BbBaseSelect.md +71 -16
  136. package/dist/ai/source/BbBaseSwitchGroup.md +4 -0
  137. package/dist/ai/source/BbBaseTimePickerInput.md +5 -1
  138. package/dist/ai/source/BbBreadcrumbs.md +31 -0
  139. package/dist/ai/source/BbButton.md +51 -24
  140. package/dist/ai/source/BbCheckbox.md +15 -1
  141. package/dist/ai/source/BbCheckboxGroup.md +2 -1
  142. package/dist/ai/source/BbCollapsible.md +7 -1
  143. package/dist/ai/source/BbColorInput.md +3 -4
  144. package/dist/ai/source/BbColorPalette.md +3 -4
  145. package/dist/ai/source/BbDatePicker.md +5 -1
  146. package/dist/ai/source/BbDatePickerInput.md +5 -1
  147. package/dist/ai/source/BbDialog.md +175 -19
  148. package/dist/ai/source/BbDropdown.md +376 -69
  149. package/dist/ai/source/BbDropdownGroup.md +330 -59
  150. package/dist/ai/source/BbForm.md +93 -5
  151. package/dist/ai/source/BbIcon.md +46 -36
  152. package/dist/ai/source/BbNumberInput.md +3 -4
  153. package/dist/ai/source/BbOffCanvas.md +474 -194
  154. package/dist/ai/source/BbPopover.md +24 -17
  155. package/dist/ai/source/BbProgress.md +2 -2
  156. package/dist/ai/source/BbRadio.md +15 -1
  157. package/dist/ai/source/BbRadioGroup.md +3 -1
  158. package/dist/ai/source/BbSelect.md +50 -2
  159. package/dist/ai/source/BbSelectPopover.md +171 -23
  160. package/dist/ai/source/BbSlider.md +3 -4
  161. package/dist/ai/source/BbSwitch.md +15 -1
  162. package/dist/ai/source/BbSwitchGroup.md +8 -5
  163. package/dist/ai/source/BbTable.md +5092 -1446
  164. package/dist/ai/source/BbTabs.md +6 -7
  165. package/dist/ai/source/BbTabsList.md +6 -7
  166. package/dist/ai/source/BbTabsPanes.md +6 -7
  167. package/dist/ai/source/BbTabsRoot.md +6 -7
  168. package/dist/ai/source/BbTag.md +3 -4
  169. package/dist/ai/source/BbTextInput.md +3 -4
  170. package/dist/ai/source/BbTextarea.md +3 -4
  171. package/dist/ai/source/BbTimePicker.md +4 -1
  172. package/dist/ai/source/BbTimePickerInput.md +5 -1
  173. package/dist/ai/source/BbToast.md +3 -1
  174. package/dist/ai/source/BbTooltip.md +5 -1
  175. package/dist/ai/source/CommonInputWrapper.md +4 -0
  176. package/dist/ai/source/CommonPopover.md +6 -0
  177. package/dist/ai/source/FlatListBox.md +11 -5
  178. package/dist/ai/source/GroupedListBox.md +7 -5
  179. package/dist/components/BbAlert/BbAlert.vue_vue_type_script_setup_true_lang.js +4 -4
  180. package/dist/components/BbAlert/types.d.ts +10 -1
  181. package/dist/components/BbAvatar/BbAvatar.vue.d.ts +1 -1
  182. package/dist/components/BbAvatar/BbAvatar.vue_vue_type_script_setup_true_lang.js +33 -33
  183. package/dist/components/BbBadge/BadgeBodyContent.vue_vue_type_script_setup_true_lang.js +22 -16
  184. package/dist/components/BbBadge/BbBadge.vue_vue_type_script_setup_true_lang.js +36 -15
  185. package/dist/components/BbBadge/badgeButtonContext.d.ts +21 -8
  186. package/dist/components/BbBadge/types.d.ts +8 -2
  187. package/dist/components/BbBaseButton/BbBaseButton.vue_vue_type_script_setup_true_lang.js +47 -47
  188. package/dist/components/BbBaseButton/RouterComponent.vue_vue_type_script_setup_true_lang.js +14 -14
  189. package/dist/components/BbBaseCheckboxGroup/BbBaseCheckboxGroup.vue_vue_type_script_setup_true_lang.js +30 -28
  190. package/dist/components/BbBaseCheckboxGroup/types.d.ts +2 -2
  191. package/dist/components/BbBaseDatePicker/BbBaseDatePicker.vue_vue_type_script_setup_true_lang.js +167 -167
  192. package/dist/components/BbBaseDatePicker/BbBaseDatePickerInputDaySelector.vue.d.ts +9 -9
  193. package/dist/components/BbBaseDatePicker/BbBaseDatePickerInputDaySelector.vue_vue_type_script_setup_true_lang.js +78 -78
  194. package/dist/components/BbBaseDatePicker/BbBaseDatePickerMonthSelector.vue_vue_type_script_setup_true_lang.js +64 -64
  195. package/dist/components/BbBaseDatePickerInput/BbBaseDatePickerInput.vue.d.ts +1 -1
  196. package/dist/components/BbBaseDatePickerInput/BbBaseDatePickerInput.vue_vue_type_script_setup_true_lang.js +138 -138
  197. package/dist/components/BbBaseInputContainer/BbBaseInputContainer.vue_vue_type_script_setup_true_lang.js +2 -2
  198. package/dist/components/BbBaseRadioGroup/BbBaseRadioGroup.vue_vue_type_script_setup_true_lang.js +24 -22
  199. package/dist/components/BbBaseRating/BbBaseRating.vue_vue_type_script_setup_true_lang.js +40 -40
  200. package/dist/components/BbBaseSelect/BbBaseSelect.vue_vue_type_script_setup_true_lang.js +74 -65
  201. package/dist/components/BbBaseSelect/types.d.ts +22 -0
  202. package/dist/components/BbBaseSlider/BbBaseSlider.vue_vue_type_script_setup_true_lang.js +5 -5
  203. package/dist/components/BbBaseSwitchGroup/BbBaseSwitchGroup.vue_vue_type_script_setup_true_lang.js +30 -28
  204. package/dist/components/BbBaseTimePickerInput/BbBaseTimePickerInput.vue.d.ts +1 -1
  205. package/dist/components/BbBaseTimePickerInput/BbBaseTimePickerInput.vue_vue_type_script_setup_true_lang.js +104 -104
  206. package/dist/components/BbBreadcrumbs/BbBreadcrumbs.vue_vue_type_script_setup_true_lang.js +35 -35
  207. package/dist/components/BbButton/BbButton.vue_vue_type_script_setup_true_lang.js +85 -81
  208. package/dist/components/BbCheckboxGroup/types.d.ts +2 -1
  209. package/dist/components/BbCollapsible/types.d.ts +7 -1
  210. package/dist/components/BbColorInput/BbColorInput.vue.d.ts +1 -1
  211. package/dist/components/BbColorInput/BbColorInput.vue_vue_type_script_setup_true_lang.js +18 -18
  212. package/dist/components/BbColorPalette/BbColorPalette.vue.d.ts +2 -2
  213. package/dist/components/BbColorPalette/BbColorPalette.vue_vue_type_script_setup_true_lang.js +50 -50
  214. package/dist/components/BbConfirm/BbConfirm.vue_vue_type_script_setup_true_lang.js +17 -17
  215. package/dist/components/BbDatePicker/BbDatePicker.vue.d.ts +1 -1
  216. package/dist/components/BbDatePicker/BbDatePicker.vue_vue_type_script_setup_true_lang.js +100 -100
  217. package/dist/components/BbDatePickerInput/BbDatePickerInput.vue_vue_type_script_setup_true_lang.js +21 -21
  218. package/dist/components/BbDialog/BbDialog.vue.d.ts +1 -1
  219. package/dist/components/BbDialog/BbDialog.vue_vue_type_script_setup_true_lang.js +133 -107
  220. package/dist/components/BbDialog/types.d.ts +29 -2
  221. package/dist/components/BbDropdown/AdaptiveDropdown.vue.d.ts +2 -0
  222. package/dist/components/BbDropdown/AdaptiveDropdown.vue_vue_type_script_setup_true_lang.js +99 -100
  223. package/dist/components/BbDropdown/BbDropdown.vue.d.ts +2 -2
  224. package/dist/components/BbDropdown/BbDropdown.vue_vue_type_script_setup_true_lang.js +208 -201
  225. package/dist/components/BbDropdown/BbDropdownList.vue_vue_type_script_setup_true_lang.js +225 -208
  226. package/dist/components/BbDropdown/DropdownPipelineResolver.vue_vue_type_script_setup_true_lang.js +21 -21
  227. package/dist/components/BbDropdown/types.d.ts +20 -5
  228. package/dist/components/BbDropdownButton/BbDropdownButton.vue_vue_type_script_setup_true_lang.js +5 -5
  229. package/dist/components/BbIcon/BbIcon.vue_vue_type_script_setup_true_lang.js +46 -46
  230. package/dist/components/BbIcon/iconCache.d.ts +33 -0
  231. package/dist/components/BbIcon/iconCache.js +17 -0
  232. package/dist/components/BbNumberInput/BbNumberInput.vue.d.ts +1 -1
  233. package/dist/components/BbNumberInput/BbNumberInput.vue_vue_type_script_setup_true_lang.js +15 -15
  234. package/dist/components/BbOffCanvas/BbOffCanvas.vue.d.ts +1 -1
  235. package/dist/components/BbOffCanvas/BbOffCanvas.vue_vue_type_script_setup_true_lang.js +254 -208
  236. package/dist/components/BbOffCanvas/types.d.ts +30 -5
  237. package/dist/components/BbPagination/BbPagination.vue.d.ts +1 -1
  238. package/dist/components/BbPopover/BbPopover.vue.d.ts +2 -2
  239. package/dist/components/BbPopover/BbPopover.vue_vue_type_script_setup_true_lang.js +149 -146
  240. package/dist/components/BbPopover/types.d.ts +3 -2
  241. package/dist/components/BbRadioGroup/types.d.ts +3 -1
  242. package/dist/components/BbSelect/BbSelect.vue_vue_type_script_setup_true_lang.js +27 -23
  243. package/dist/components/BbSelect/types.d.ts +32 -0
  244. package/dist/components/BbSelectPopover/BbSelectPopover.vue_vue_type_script_setup_true_lang.js +221 -213
  245. package/dist/components/BbSlider/BbSlider.vue.d.ts +1 -1
  246. package/dist/components/BbSlider/BbSlider.vue_vue_type_script_setup_true_lang.js +20 -20
  247. package/dist/components/BbTable/BbTable.vue.d.ts +10 -4
  248. package/dist/components/BbTable/BbTable.vue_vue_type_script_setup_true_lang.js +1433 -600
  249. package/dist/components/BbTable/BbTableDataRow.d.ts +162 -0
  250. package/dist/components/BbTable/BbTableDataRow.js +136 -0
  251. package/dist/components/BbTable/BbTableExpandRow.d.ts +73 -0
  252. package/dist/components/BbTable/BbTableExpandRow.js +52 -0
  253. package/dist/components/BbTable/types.d.ts +324 -111
  254. package/dist/components/BbTable/utils.d.ts +92 -4
  255. package/dist/components/BbTable/utils.js +71 -36
  256. package/dist/components/BbTabs/types.d.ts +0 -8
  257. package/dist/components/BbTag/BbTag.vue.d.ts +1 -1
  258. package/dist/components/BbTag/BbTag.vue_vue_type_script_setup_true_lang.js +18 -18
  259. package/dist/components/BbTextInput/BbTextInput.vue.d.ts +1 -1
  260. package/dist/components/BbTextInput/BbTextInput.vue_vue_type_script_setup_true_lang.js +18 -18
  261. package/dist/components/BbTextarea/BbTextarea.vue.d.ts +1 -1
  262. package/dist/components/BbTextarea/BbTextarea.vue_vue_type_script_setup_true_lang.js +54 -54
  263. package/dist/components/BbTimePicker/BbTimePicker.vue.d.ts +1 -1
  264. package/dist/components/BbTimePicker/BbTimePicker.vue_vue_type_script_setup_true_lang.js +80 -80
  265. package/dist/components/BbTimePickerInput/BbTimePickerInput.vue_vue_type_script_setup_true_lang.js +61 -61
  266. package/dist/components/BbToast/BbToast.vue_vue_type_script_setup_true_lang.js +17 -17
  267. package/dist/components/BbToast/BbToastMessage.vue_vue_type_script_setup_true_lang.js +13 -13
  268. package/dist/components/BbTooltip/types.d.ts +5 -1
  269. package/dist/components/ChipsBox/ChipsBox.vue_vue_type_script_setup_true_lang.js +17 -17
  270. package/dist/components/ClearableButton/ClearableButton.vue_vue_type_script_setup_true_lang.js +5 -5
  271. package/dist/components/CommonPopover/CommonPopover.vue_vue_type_script_setup_true_lang.js +2 -1
  272. package/dist/components/FlatListBox/FlatListBox.vue_vue_type_script_setup_true_lang.js +83 -83
  273. package/dist/components/GroupedListBox/GroupedListBox.vue_vue_type_script_setup_true_lang.js +56 -56
  274. package/dist/composables/useBaseOptions.js +30 -20
  275. package/dist/composables/useBbConfig.d.ts +27 -6
  276. package/dist/composables/useBbConfig.js +8 -2
  277. package/dist/composables/useConfig.d.ts +59 -0
  278. package/dist/composables/useConfig.js +9 -0
  279. package/dist/composables/useItemsGetter.d.ts +11 -0
  280. package/dist/composables/useItemsGetter.js +44 -18
  281. package/dist/composables/useLocale.js +10 -7
  282. package/dist/composables/useMobile.js +5 -5
  283. package/dist/composables/usePrefill.d.ts +21 -2
  284. package/dist/composables/usePrefill.js +25 -19
  285. package/dist/composables/useSsrState.d.ts +20 -0
  286. package/dist/composables/useSsrState.js +13 -0
  287. package/dist/composables/useTableWidthContext.d.ts +24 -4
  288. package/dist/composables/useTableWidthContext.js +31 -28
  289. package/dist/composables/useToast.js +5 -5
  290. package/dist/deprecation/ai-deprecations.json.d.ts +121 -11
  291. package/dist/deprecation/ai-deprecations.json.js +1 -1
  292. package/dist/directives/bbDropdown.d.ts +15 -4
  293. package/dist/directives/bbDropdown.js +31 -9
  294. package/dist/directives/createPopoverDirective.d.ts +62 -0
  295. package/dist/directives/createPopoverDirective.js +63 -23
  296. package/dist/i18n/locales/cs.json +4 -1
  297. package/dist/i18n/locales/da.json +4 -1
  298. package/dist/i18n/locales/de.json +4 -1
  299. package/dist/i18n/locales/el.json +4 -1
  300. package/dist/i18n/locales/en.json +4 -1
  301. package/dist/i18n/locales/es.json +4 -1
  302. package/dist/i18n/locales/fi.json +4 -1
  303. package/dist/i18n/locales/fr.json +4 -1
  304. package/dist/i18n/locales/hu.json +4 -1
  305. package/dist/i18n/locales/it.json +4 -1
  306. package/dist/i18n/locales/ja.json +4 -1
  307. package/dist/i18n/locales/ko.json +4 -1
  308. package/dist/i18n/locales/nb.json +4 -1
  309. package/dist/i18n/locales/nl.json +4 -1
  310. package/dist/i18n/locales/pl.json +4 -1
  311. package/dist/i18n/locales/pt.json +4 -1
  312. package/dist/i18n/locales/ro.json +4 -1
  313. package/dist/i18n/locales/ru.json +4 -1
  314. package/dist/i18n/locales/sv.json +4 -1
  315. package/dist/i18n/locales/tr.json +4 -1
  316. package/dist/i18n/locales/uk.json +4 -1
  317. package/dist/i18n/locales/zh-cn.json +4 -1
  318. package/dist/index.d.ts +7 -2
  319. package/dist/index.js +69 -67
  320. package/dist/llms-full.txt +3997 -1458
  321. package/dist/llms-medium.txt +140 -73
  322. package/dist/llms.txt +7 -6
  323. package/dist/locale-blueprint.json +4 -1
  324. package/dist/nuxt-auto-imports.d.ts +100 -0
  325. package/dist/nuxt-auto-imports.js +55 -0
  326. package/dist/nuxt-module.d.ts +22 -1
  327. package/dist/nuxt.js +22 -8
  328. package/dist/plugin.d.ts +10 -1
  329. package/dist/plugin.js +20 -19
  330. package/dist/runtime/nuxt-plugin.js +28 -21
  331. package/dist/styles.css +1 -1
  332. package/dist/utilities/misc/listboxMetrics.d.ts +3 -3
  333. package/dist/utilities/misc/listboxMetrics.js +2 -2
  334. package/dist/utilities/misc/ssrStateInjectionKey.d.ts +42 -0
  335. package/dist/utilities/misc/ssrStateInjectionKey.js +4 -0
  336. package/dist/validated/BbForm.vue_vue_type_script_setup_true_lang.js +45 -27
  337. package/dist/validated/BbSelect.vue.d.ts +2 -0
  338. package/dist/validated/BbSelect.vue_vue_type_script_setup_true_lang.js +1 -0
  339. package/dist/validated/useValidatedField.js +25 -23
  340. package/dist/vite.js +7 -3
  341. package/llms.txt +8 -7
  342. package/package.json +2 -2
  343. package/scripts/lib/eslint-plugin.d.ts +19 -1
  344. package/scripts/lib/eslint-plugin.mjs +155 -14
  345. package/scripts/lib/html-attributes.mjs +91 -0
  346. package/scripts/lib/validate-bb-markup.mjs +59 -11
  347. package/dist/assets/svgs/sort.svg_raw.js +0 -4
  348. package/dist/components/BbBadge/BadgeAppendIcon.vue.d.ts +0 -3
  349. package/dist/components/BbBadge/BadgeAppendIcon.vue.js +0 -5
  350. package/dist/components/BbBadge/BadgeAppendIcon.vue_vue_type_script_setup_true_lang.js +0 -22
@@ -333,11 +333,14 @@ export type InternalGroup = DropdownEntryBase & {
333
333
  * predictable size regardless of row content. A `number` is pixels; a `string`
334
334
  * is any CSS length (`'50vh'`, `'20rem'`). `'none'` (or `0`) removes the cap.
335
335
  *
336
- * `searchable` groups default to `280` (≈ 10 rows of 28px), since a searched
337
- * list is usually long; set your own value to tweak, or `'none'` to uncap.
338
- * Non-searchable groups are uncapped unless you set this.
336
+ * `searchable` groups default to `280` (≈ 10 rows of 28px) **on the flyout**,
337
+ * since a searched list is usually long; set your own value to tweak, or
338
+ * `'none'` to uncap. The adaptive sheet ignores the cap so the OffCanvas
339
+ * body stays the only scroller (nested group overflow trapped swipes that
340
+ * started on a row). Non-searchable flyout groups are uncapped unless you
341
+ * set this.
339
342
  *
340
- * @defaultValue `280` for `searchable` groups, otherwise unset
343
+ * @defaultValue `280` for `searchable` flyout groups, otherwise unset
341
344
  */
342
345
  maxHeight?: number | string;
343
346
  /**
@@ -506,6 +509,17 @@ export type BbDropdownProps = {
506
509
  * @defaultValue `config.adaptive` (`true`)
507
510
  */
508
511
  adaptive?: boolean;
512
+ /**
513
+ * Apply the compact density variant — the same ladder `BbSelect` uses, on
514
+ * both surfaces: flyout rows go 28px → 24px, sheet rows 44px → 36px. The
515
+ * label size does not change; density here is height, as it is for a select.
516
+ *
517
+ * A menu row is a touch target in a way a select option is not quite — a
518
+ * mis-tap fires the neighbouring COMMAND rather than picking the wrong value
519
+ * — so the compact sheet stops at 36px and never follows the flyout down to
520
+ * 24px. That is the same floor the select's sheet holds.
521
+ */
522
+ compact?: boolean;
509
523
  /**
510
524
  * Element the panel positions itself against, when it should differ from the
511
525
  * activator — e.g. a wrapper that the activator is only a part of, so the menu
@@ -585,7 +599,8 @@ export type BbDropdownProps = {
585
599
  padding?: number;
586
600
  /**
587
601
  * Position of the dropdown relative to the activator.
588
- * Use `'cursor'` to anchor at the mouse/touch coordinates (hides arrow).
602
+ * Use `'cursor'` to anchor at the mouse/touch coordinates.
603
+ * (No arrow is drawn either way — arrows are tooltip-only in v3.)
589
604
  *
590
605
  * @defaultValue `'bottom'`
591
606
  */
@@ -786,7 +801,9 @@ export type BbDropdownGroupEvents = {
786
801
  ## Styles
787
802
 
788
803
  ```css
789
- /* @inherit --transition-duration — set inline by the adaptive surface */
804
+ /* @inherit --transition-duration — set inline by the adaptive surface;
805
+ @inherit --py — the adaptive sheet's rules target BbOffCanvas elements, which
806
+ declare it on the panel */
790
807
  .bb-dropdown {
791
808
  overflow: visible;
792
809
 
@@ -999,7 +1016,11 @@ export type BbDropdownGroupEvents = {
999
1016
  * is inert there and `max-height` alone caps it. */
1000
1017
  flex: 1 1 auto;
1001
1018
  flex-direction: column;
1002
- font-size: calc(var(--bb-fs) - 1px);
1019
+ /* `--bb-fs`, matching the select's option label. These two menus sit at
1020
+ the same 28px row height on the same screen and their labels were a
1021
+ pixel apart (13 against 14) — the desktop twin of the sheet drift, and
1022
+ the kind of difference nobody can name but everybody sees. */
1023
+ font-size: var(--bb-fs);
1003
1024
  gap: 1px;
1004
1025
  line-height: 1.2;
1005
1026
  max-height: var(--max-h, none);
@@ -1092,9 +1113,11 @@ export type BbDropdownGroupEvents = {
1092
1113
  /* Keep natural row height under a capped, scrolling container. */
1093
1114
  flex-shrink: 0;
1094
1115
  height: 28px;
1095
- /* Let the row sit inside a fixed `--w` panel instead of expanding it. */
1116
+ /* Let the row sit inside a fixed `--w` panel instead of expanding it.
1117
+ * Also the containing block for the absolute selection check. */
1096
1118
  min-width: 0;
1097
1119
  padding: 2px 6px;
1120
+ position: relative;
1098
1121
  text-align: left;
1099
1122
  width: 100%;
1100
1123
 
@@ -1132,8 +1155,7 @@ export type BbDropdownGroupEvents = {
1132
1155
  }
1133
1156
 
1134
1157
  .item__prepend-icon,
1135
- .item__append-icon,
1136
- .item__check {
1158
+ .item__append-icon {
1137
1159
  --size: 16px;
1138
1160
  flex-shrink: 0;
1139
1161
  font-size: 15px;
@@ -1179,21 +1201,42 @@ export type BbDropdownGroupEvents = {
1179
1201
 
1180
1202
  .item__prepend-icon,
1181
1203
  .item__append-icon,
1182
- .item__check,
1183
1204
  .item__chevron {
1184
1205
  margin-top: 2px;
1185
1206
  }
1207
+
1208
+ /* Absolute check: pin to the title line, not the row mid-point. */
1209
+ .item__check {
1210
+ margin-top: 0;
1211
+ top: 6px;
1212
+ transform: none;
1213
+ }
1186
1214
  }
1187
1215
 
1188
- .item__check,
1189
1216
  .item__chevron {
1190
1217
  --size: 16px;
1191
1218
  color: var(--bb-text-muted);
1219
+ flex-shrink: 0;
1220
+ font-size: 19px;
1192
1221
  margin-inline-start: auto;
1193
1222
  }
1194
1223
 
1224
+ /*
1225
+ * Selection check is out of flow. Selectable groups always mount the
1226
+ * glyph (stroke animates in when selected); leaving it in flex would
1227
+ * reserve ~24px on EVERY row and make ellipsis truncate as if an
1228
+ * append icon were present. Absolute + pad-only-when-selected lets
1229
+ * unselected labels use the full row.
1230
+ */
1195
1231
  .item__check {
1232
+ --size: 16px;
1196
1233
  color: var(--bb-text);
1234
+ font-size: 15px;
1235
+ inset-inline-end: 6px;
1236
+ pointer-events: none;
1237
+ position: absolute;
1238
+ top: 50%;
1239
+ transform: translateY(-50%);
1197
1240
 
1198
1241
  path {
1199
1242
  stroke-dasharray: 30;
@@ -1204,16 +1247,14 @@ export type BbDropdownGroupEvents = {
1204
1247
  }
1205
1248
 
1206
1249
  &.bb-dropdown__item--selected {
1250
+ /* Keep the label clear of the absolute check. */
1251
+ padding-inline-end: calc(6px + 16px + 4px);
1252
+
1207
1253
  .item__check path {
1208
1254
  stroke-dashoffset: 0;
1209
1255
  }
1210
1256
  }
1211
1257
 
1212
- .item__chevron {
1213
- flex-shrink: 0;
1214
- font-size: 19px;
1215
- }
1216
-
1217
1258
  &.bb-dropdown__item--variant-destructive {
1218
1259
  color: var(--bb-danger);
1219
1260
 
@@ -1251,6 +1292,24 @@ export type BbDropdownGroupEvents = {
1251
1292
  }
1252
1293
  }
1253
1294
 
1295
+ /* ---- Compact density --------------------------------------------------
1296
+ * The same ladder `BbSelect` walks, so a dense toolbar can hold a select and a
1297
+ * menu that agree: the flyout drops 28px → 24px here, and the SHEET drops
1298
+ * 44px → 36px through `--sheet-row-h` (AdaptiveDropdown reads
1299
+ * `listboxOptionHeight`, the helper the listbox reads, so neither can drift).
1300
+ * The label does not shrink — density is height, exactly as it is for a select
1301
+ * — and the sheet never follows the flyout to 24px, because a menu row is a
1302
+ * touch target whose mis-tap fires the neighbouring COMMAND. */
1303
+ .bb-dropdown.bb-dropdown--compact {
1304
+ .bb-dropdown__item {
1305
+ height: 24px;
1306
+ }
1307
+
1308
+ .bb-dropdown__placeholder {
1309
+ min-height: 24px;
1310
+ }
1311
+ }
1312
+
1254
1313
  /* ---- Adaptive drill-down sheet ----------------------------------------
1255
1314
  * The mobile surface (`AdaptiveDropdown`) reuses the `.bb-dropdown` classes
1256
1315
  * above by carrying `.bb-dropdown` on the off-canvas root; these rules only
@@ -1260,7 +1319,104 @@ export type BbDropdownGroupEvents = {
1260
1319
  /* Shrink the off-canvas chrome so the sheet reads as compact as the desktop
1261
1320
  panel rather than a full dialog. */
1262
1321
  --dialog-gap: 8px;
1263
- --dialog-title-fs: calc(var(--bb-fs) - 1px);
1322
+ /* The title tracks the row label rather than the off-canvas default. It was
1323
+ pulled DOWN to `--bb-fs - 1` (13px) once, which put it under the rows it
1324
+ heads; the default (`--bb-fs + 2`) fixed that until the rows moved to 18px
1325
+ and passed it again. Tied to the same constant, a title can no longer end
1326
+ up smaller than the list beneath it whatever that constant becomes. */
1327
+ --dialog-title-fs: var(--sheet-option-fs, 1rem);
1328
+
1329
+ /* Root sheet title via `offCanvasProps.title` (no drill nav / custom
1330
+ `#header`): centre it and tighten the title→divider air like BbSelect.
1331
+ `:not(:has(.bb-dropdown__nav))` keeps consumer `#header` content and the
1332
+ drill nav on their own layouts. */
1333
+ .bb-offcanvas__header:not(:has(.bb-dropdown__nav)) {
1334
+ border-bottom: var(--bb-border-w) solid var(--bb-border);
1335
+ padding-bottom: calc(var(--py) - 6px);
1336
+
1337
+ > .bb-offcanvas__title {
1338
+ flex: 1 1 auto;
1339
+ text-align: center;
1340
+ }
1341
+ }
1342
+
1343
+ /* Drill nav owns the title→body air. Drop the OffCanvas header's own bottom
1344
+ pad so it does not stack under the nav and push chips / search below
1345
+ BbSelect's. */
1346
+ .bb-offcanvas__header:has(.bb-dropdown__nav--drill) {
1347
+ padding-bottom: 0;
1348
+ }
1349
+
1350
+ /* Body used to start flush under the header. Match BbSelect's step down
1351
+ from the title rule. Specificity beats OffCanvas's nested shorthand.
1352
+ Skip headerless sheets (`--no-header`): OffCanvas already supplies the
1353
+ top inset (and the handle clearance) that this would otherwise crush to
1354
+ 0.5rem — the filters root with no title / drill nav / `#header`. */
1355
+ &.bb-offcanvas:not(.bb-offcanvas--no-header)
1356
+ .bb-offcanvas__body
1357
+ .bb-offcanvas__body-content {
1358
+ padding-top: 0.5rem;
1359
+ }
1360
+
1361
+ /* Flat multi-group sheets: space a following section after the previous
1362
+ group's items/search. */
1363
+ .bb-dropdown__group-items + .bb-dropdown__group-title,
1364
+ .bb-dropdown__group-search + .bb-dropdown__group-title,
1365
+ .bb-dropdown__divider + .bb-dropdown__group-title {
1366
+ margin-top: 0.5rem;
1367
+ }
1368
+
1369
+ /* Group headers on the sheet read as BbSelect's sheet label — centred title
1370
+ weight/size, not the flyout's faint left-aligned section caption. No
1371
+ hairline here: the select sheet label's rule lives on the OffCanvas
1372
+ header, and repeating it under every group stacked like extra chrome.
1373
+ Nested drills with `hideLabel` skip this entirely; the drill nav title is
1374
+ already that label. */
1375
+ .bb-dropdown__group-title {
1376
+ align-items: center;
1377
+ border-bottom: 0;
1378
+ color: var(--bb-text);
1379
+ column-gap: 6px;
1380
+ display: flex;
1381
+ font-size: var(--dialog-title-fs, var(--sheet-option-fs, 1rem));
1382
+ font-weight: 500;
1383
+ justify-content: center;
1384
+ letter-spacing: normal;
1385
+ /* Match select's title→body air (`padding-bottom: calc(var(--py) - 6px)`
1386
+ on the OffCanvas header) so the label sits above the soft search the
1387
+ same way City sits above its field. */
1388
+ padding: 0.25rem 0 calc(var(--py, var(--bb-panel-p)) - 6px);
1389
+
1390
+ .group-title__content {
1391
+ flex: 1 1 auto;
1392
+ line-height: normal;
1393
+ overflow-x: clip;
1394
+ overflow-y: visible;
1395
+ text-align: center;
1396
+ }
1397
+
1398
+ .group-title__prepend-icon,
1399
+ .group-title__append-icon {
1400
+ --size: 1.25rem;
1401
+ font-size: var(--size);
1402
+ }
1403
+ }
1404
+
1405
+ /* Headerless sheet: the first group title IS the sheet label (Flat filters'
1406
+ "Status" beside BbSelect's "City"). Body already clears the handle via
1407
+ `--no-header` padding (`calc(var(--py) + 12px)`); the items-container's
1408
+ 4px (meant for air under a nav divider) and the title's own 0.25rem top
1409
+ pad were stacking on top and dropping the label ~8px below City.
1410
+ Measured: City title @29px, Flat Status @33–37px before this. */
1411
+ &.bb-offcanvas--no-header .bb-dropdown__items-container {
1412
+ padding-top: 0;
1413
+ }
1414
+
1415
+ &.bb-offcanvas--no-header
1416
+ .bb-dropdown__items-container
1417
+ > .bb-dropdown__group-title:first-child {
1418
+ padding-top: 0;
1419
+ }
1264
1420
 
1265
1421
  /* Clip the sideways level slide so it never spills a horizontal scrollbar. */
1266
1422
  .bb-dropdown__adaptive-body {
@@ -1290,55 +1446,132 @@ export type BbDropdownGroupEvents = {
1290
1446
  * construction, so this selector needs no viewport clause of its own. The
1291
1447
  * JS half lives in `useTouchRowLadder`, for the virtualized option lists
1292
1448
  * whose row heights feed a virtualizer instead of a stylesheet. */
1449
+ /* Belt for an explicit `maxHeight` leaking onto the sheet: never nest a
1450
+ second scrollport inside the OffCanvas body. */
1451
+ .bb-dropdown__group-items--capped {
1452
+ display: contents;
1453
+ max-height: none;
1454
+ overflow: visible;
1455
+ }
1456
+
1293
1457
  .bb-dropdown__items-container {
1294
- font-size: calc(var(--bb-fs) + 1px);
1458
+ /* Published by AdaptiveDropdown from `listboxMetrics`, the same helpers the
1459
+ select's sheet reads. The fallbacks are the shipped touch values, so the
1460
+ stylesheet still reads on its own. */
1461
+ font-size: var(--sheet-option-fs, 1rem);
1295
1462
  max-height: none;
1296
1463
  min-width: 0;
1297
1464
  overflow-y: visible;
1298
- padding: 0;
1465
+ /* Horizontal inset is the OffCanvas body's `--px`. Do not re-pad here
1466
+ (or on the rows) to fake alignment with search/chips. */
1467
+ padding: 4px 0 0;
1299
1468
  width: 100%;
1300
1469
 
1301
1470
  .bb-dropdown__item {
1302
- height: 44px;
1303
- padding: 2px 10px;
1471
+ height: var(--sheet-row-h, 44px);
1472
+ /* Flyout rows are `2px 6px`. +4px start so the label shares the
1473
+ search field's text edge (`padding-inline: 0.75rem` on the fill). */
1474
+ padding-left: calc(6px + 4px);
1475
+ /* iOS treats a <button> as a tap target and will not hand the pan to
1476
+ the sheet body unless the control itself opts into vertical panning.
1477
+ Without this, dismiss/scroll work on the handle and miss on rows. */
1478
+ touch-action: pan-y;
1304
1479
 
1305
1480
  &.bb-dropdown__item--description {
1306
- min-height: 44px;
1481
+ min-height: var(--sheet-row-h, 44px);
1307
1482
  }
1308
1483
 
1484
+ .item__content {
1485
+ line-height: normal;
1486
+ overflow-x: clip;
1487
+ overflow-y: visible;
1488
+ }
1489
+
1490
+ /* One step under the row label, the same step the listbox puts between
1491
+ an option and its group header — so the two sheets' secondary text
1492
+ agrees as well as their primary. */
1309
1493
  .item__description {
1310
- font-size: calc(var(--bb-fs) - 1px);
1494
+ font-size: calc(var(--sheet-option-fs, 1rem) - 2px);
1311
1495
  line-height: 18px;
1312
1496
  }
1313
1497
  }
1314
1498
 
1315
1499
  .bb-dropdown__placeholder {
1316
- min-height: 44px;
1317
- padding: 2px 10px;
1500
+ min-height: var(--sheet-row-h, 44px);
1501
+ padding-left: calc(6px + 4px);
1318
1502
  }
1319
1503
 
1320
- .bb-dropdown__group-title {
1321
- font-size: calc(var(--bb-fs) - 1px);
1322
- padding: 8px 10px 4px;
1504
+ /* Icons scale with the label they sit beside. The desktop sizes (16px
1505
+ glyphs against a 13px label) were tuned for a 28px row; left alone in a
1506
+ 44px row with an 18px label they read as undersized — the chevron that
1507
+ says "this opens another level" ended up the smallest thing in the row.
1508
+ One step up, so the glyph keeps roughly the proportion to its label that
1509
+ it has on the desktop panel. BbIcon reads `--size`; keep `font-size` on
1510
+ the same token so the glyph box and the type size cannot drift. */
1511
+ .item__prepend-icon,
1512
+ .item__append-icon,
1513
+ .item__check,
1514
+ .item__chevron {
1515
+ --size: 1.25rem;
1516
+ font-size: var(--size);
1323
1517
  }
1518
+
1519
+ /* Group titles are restyled as select-sheet labels on `--adaptive`
1520
+ (see block above); do not re-apply the flyout caption metrics here. */
1324
1521
  }
1325
1522
 
1523
+ /* Soft filled search — mirrors BbSelectPopover's sheet field: no underline
1524
+ rule, faint `--bb-muted` wash, and the same inset as sheet chips / row
1525
+ padding so the fill lines up. */
1326
1526
  .bb-dropdown__group-search {
1327
- padding: 8px 10px;
1527
+ background-color: var(--bb-muted);
1528
+ border-bottom: 0;
1529
+ border-radius: var(--bb-radius);
1530
+ /* Undo the flyout's full-bleed negative margins. No extra inline inset —
1531
+ the OffCanvas body already pads `--px`. */
1532
+ margin: 0 0 0.25rem;
1533
+ padding: 0.625rem 0.75rem;
1534
+ position: relative;
1535
+ top: auto;
1536
+ transition: background-color var(--bb-transition-fast) ease;
1537
+ /* Flyout search is sticky `z-index: 1`; that same stacking painted over
1538
+ the sheet's grab handle once the body scrolled. */
1539
+ z-index: auto;
1540
+
1541
+ /* One emphasis stop above the resting --bb-muted (6%) fill. */
1542
+ &:focus-within {
1543
+ background-color: color-mix(in oklab, var(--bb-text) 8%, var(--bb-panel));
1544
+ }
1545
+
1546
+ /* Lens on the trailing edge, same side as BbSelect's sheet search. */
1547
+ .bb-dropdown__search-icon {
1548
+ order: 1;
1549
+ }
1550
+
1551
+ .bb-dropdown__search-input {
1552
+ font-size: var(--bb-input-fs);
1553
+ order: 0;
1554
+ }
1555
+
1556
+ /* Busy strip sits on the field's bottom edge (no border to overlay). */
1557
+ .bb-dropdown__group-loading {
1558
+ border-radius: 0 0 var(--bb-radius) var(--bb-radius);
1559
+ bottom: 0;
1560
+ }
1328
1561
  }
1329
1562
 
1330
1563
  /* Removable selection chips for the current multi-select level — sit above the
1331
1564
  menu (and its search), mirroring BbSelect's sheet. */
1332
1565
  .bb-dropdown__sheet-selection {
1333
1566
  flex-shrink: 0;
1334
- padding: 0 4px 8px;
1567
+ padding: 0 0 0.5rem;
1335
1568
  text-align: left;
1336
1569
  }
1337
1570
 
1338
1571
  .bb-dropdown__sheet-chips {
1339
1572
  display: flex;
1340
1573
  flex-wrap: wrap;
1341
- gap: 8px;
1574
+ gap: 0.5rem;
1342
1575
  }
1343
1576
 
1344
1577
  /* Touch target: the xl badge is a ~24px pill — fine inline on desktop, too
@@ -1347,9 +1580,9 @@ export type BbDropdownGroupEvents = {
1347
1580
  padding and a bigger clear glyph; the badge API stays untouched. Mirrors
1348
1581
  BbSelectPopover's sheet chips. */
1349
1582
  .bb-dropdown__sheet-chip {
1350
- --min-size: 34px;
1351
- --padding-inline: 12px;
1352
- --clear-size: 20px;
1583
+ --min-size: 2.125rem;
1584
+ --padding-inline: 0.75rem;
1585
+ --clear-size: 1.25rem;
1353
1586
  }
1354
1587
 
1355
1588
  .bb-dropdown__nav {
@@ -1357,42 +1590,69 @@ export type BbDropdownGroupEvents = {
1357
1590
  column-gap: 6px;
1358
1591
  display: flex;
1359
1592
  width: 100%;
1360
- }
1361
1593
 
1362
- .bb-dropdown__nav-back {
1363
- align-items: center;
1364
- background: transparent;
1365
- border: none;
1366
- border-radius: var(--bb-radius-sm);
1367
- color: var(--bb-text);
1368
- cursor: pointer;
1369
- display: inline-flex;
1370
- flex-shrink: 0;
1371
- /* Touch target: this is the sheet's only way back up a level. */
1372
- height: 40px;
1373
- justify-content: center;
1374
- margin-inline-start: -8px;
1375
- width: 40px;
1594
+ /* Drilled in: a navigation bar. The TITLE LINE sets the bar height — same
1595
+ as BbSelect's OffCanvas title — so City and Status share one baseline.
1596
+ The back control is a BbButton ghost sm, absolutely pinned to the
1597
+ OffCanvas header with equal inset from the sheet's top and left. */
1598
+ &--drill {
1599
+ /* Title line sets the bar height — same as BbSelect's OffCanvas title.
1600
+ Sit in the header's `--px`; do not pull out and re-pad to fake a
1601
+ full-bleed bar. Bottom pad matches BbSelect's
1602
+ `calc(var(--py) - 6px)`. No `position: relative` — the back button's
1603
+ containing block is the header. */
1604
+ display: block;
1605
+ padding-bottom: calc(var(--py, var(--bb-panel-p)) - 6px);
1606
+
1607
+ /* Equal inset from the sheet edges. Header is `position: relative`
1608
+ in OffCanvas. Size/chrome come from BbButton ghost sm.
1609
+ Use `--bb-panel-p`, not `--px`: BbButton redefines `--px` as its
1610
+ own padding token, which collapsed the inset to 10px. */
1611
+ .bb-dropdown__nav-back {
1612
+ left: var(--bb-panel-p);
1613
+ position: absolute;
1614
+ top: var(--bb-panel-p);
1615
+
1616
+ /* Same chevron_right asset the submenu rows use, pointed back. */
1617
+ .bb-button__icon {
1618
+ transform: rotate(180deg);
1619
+ }
1620
+ }
1376
1621
 
1377
- &:hover {
1378
- background-color: color-mix(in oklab, var(--bb-text) 8%, transparent);
1622
+ /* Spacer kept in the DOM for the drill a11y tests; layout is now the
1623
+ title's equal inline gutters below. */
1624
+ .bb-dropdown__nav-gutter {
1625
+ display: none;
1626
+ }
1379
1627
  }
1628
+ }
1380
1629
 
1381
- .bb-dropdown__nav-back-icon {
1382
- --size: 16px;
1383
- /* Reuse `chevron_right` pointed the other way (no left asset exists). */
1384
- transform: rotate(180deg);
1385
- }
1630
+ /* Pure spacer, mirroring the back button's box (root / non-drill layouts). */
1631
+ .bb-dropdown__nav-gutter {
1632
+ height: 24px;
1633
+ width: 24px;
1386
1634
  }
1387
1635
 
1388
- .bb-dropdown__nav-title {
1636
+ .bb-offcanvas__title.bb-dropdown__nav-title {
1389
1637
  flex: auto;
1390
1638
  font-weight: 500;
1639
+ /* Same 1em line box as BbSelect's sheet title so the cap sits on the
1640
+ same baseline. Descenders ("Tags") paint into the drill nav's bottom
1641
+ pad — `overflow-y: visible`; ellipsis still clips on X only. */
1391
1642
  min-width: 0;
1392
- overflow: hidden;
1643
+ overflow-x: clip;
1644
+ overflow-y: visible;
1393
1645
  text-overflow: ellipsis;
1394
1646
  white-space: nowrap;
1395
1647
  }
1648
+
1649
+ .bb-dropdown__nav--drill .bb-dropdown__nav-title {
1650
+ /* Equal side gutters matching BbButton sm icon-only (24px) so the label
1651
+ centres. */
1652
+ display: block;
1653
+ padding-inline: 24px;
1654
+ text-align: center;
1655
+ }
1396
1656
  }
1397
1657
 
1398
1658
  /* Level slide: drilling in enters from the right and the old level exits left;
@@ -1428,4 +1688,15 @@ export type BbDropdownGroupEvents = {
1428
1688
  opacity: 0;
1429
1689
  transform: translateX(28px);
1430
1690
  }
1691
+
1692
+ /*
1693
+ * Q14.3 — see `reservesCheckSpace` in BbDropdown.vue for why this is scoped to
1694
+ * an auto-width panel. `:has(.item__check)` targets exactly the rows a
1695
+ * selectable group mounts a check on, so a non-selectable group in the same
1696
+ * menu is untouched. The value matches the `--selected` rule's clearance; when
1697
+ * both apply they agree, so nothing shifts on selection.
1698
+ */
1699
+ .bb-dropdown__panel--reserve-check .bb-dropdown__item:has(.item__check) {
1700
+ padding-inline-end: calc(6px + 16px + 4px);
1701
+ }
1431
1702
  ```
@@ -27,7 +27,7 @@
27
27
  </template>
28
28
 
29
29
  <script setup lang="ts">
30
- import { useTemplateRef } from 'vue';
30
+ import { getCurrentInstance, useTemplateRef } from 'vue';
31
31
  import { useForm } from 'vee-validate';
32
32
  import { useBbFormContextHost } from '@/composables/useBbFormContext';
33
33
 
@@ -41,7 +41,10 @@ const props = defineProps<{
41
41
  id?: string;
42
42
  }>();
43
43
 
44
- const emit = defineEmits<{
44
+ // Declared for the public typing and the manifest. The handler is INVOKED
45
+ // directly (see `runSubmitListeners`) rather than through the returned `emit`,
46
+ // so the returned function is deliberately unused.
47
+ defineEmits<{
45
48
  /** Fired only when every field passes; carries the collected form values. */
46
49
  submit: [values: Record<string, unknown>];
47
50
  }>();
@@ -57,9 +60,94 @@ const {
57
60
  validate,
58
61
  } = useForm();
59
62
 
60
- // Validates all fields on native submit; emits `submit` with the values only
61
- // when the form is valid (and preventDefault's the native navigation).
62
- const onSubmit = handleSubmit((formValues) => emit('submit', formValues));
63
+ /*
64
+ * Validates all fields on native submit, then runs the consumer's `@submit`
65
+ * with the values — only when the form is valid (and preventDefault's the
66
+ * native navigation).
67
+ *
68
+ * Q18.3 — why this calls the listener instead of `emit('submit', …)`.
69
+ * vee-validate decides "is a submit still in flight?" from what the handler
70
+ * RETURNS: return a thenable and `isSubmitting` stays true until it settles;
71
+ * return nothing and it clears immediately. Vue's `emit` always returns
72
+ * undefined, so `isSubmitting` used to drop as soon as VALIDATION resolved —
73
+ * measured over 10 ticks, `[true,false,false,…]` against `[true,true,true,…]`
74
+ * for a handler whose promise is returned. Every guide and the manifest tell
75
+ * you to bind a button's `loading` to it, so the spinner flashed for one frame
76
+ * and the user could double-submit a save that was still running.
77
+ *
78
+ * `vnode.props.onSubmit` rather than `useAttrs()`: `submit` is a DECLARED
79
+ * emit, so Vue strips it from `$attrs`. The raw prop is still on the vnode.
80
+ *
81
+ * The shape is preserved in both directions — an array of listeners (two
82
+ * `@submit` handlers) is called in order, and a sync handler returns undefined
83
+ * exactly as before, so nothing about existing behaviour changes except that
84
+ * an async handler is now awaited.
85
+ */
86
+ const instance = getCurrentInstance();
87
+
88
+ type SubmitListener = (values: Record<string, unknown>) => unknown;
89
+
90
+ /** `@submit.once` compiles to a SEPARATE `onSubmitOnce` prop — Vue's own emit
91
+ * reads both and guards the once-handler with a fired flag. Mirror that, or a
92
+ * `.once` listener would silently never run. */
93
+ let onceFired = false;
94
+
95
+ /**
96
+ * Route a listener failure the way `emit` did.
97
+ *
98
+ * Vue wraps event handlers in `callWithAsyncErrorHandling`, which sends both
99
+ * sync throws and promise rejections to `app.config.errorHandler`. Calling the
100
+ * prop directly skips that, so a project with a global reporter (Sentry, a
101
+ * toast-on-error boundary) would stop seeing submit failures. Report through
102
+ * the same channel, then let the submit settle — a rejected save must still
103
+ * clear `isSubmitting`, or the button spins forever.
104
+ */
105
+ const reportListenerError = (error: unknown) => {
106
+ const handler = instance?.appContext.config.errorHandler;
107
+ if (handler)
108
+ handler(error, instance?.proxy ?? null, 'component event handler');
109
+ else console.error(error);
110
+ };
111
+
112
+ const isThenable = (value: unknown): value is PromiseLike<unknown> =>
113
+ typeof (value as PromiseLike<unknown>)?.then === 'function';
114
+
115
+ const runSubmitListeners = (formValues: Record<string, unknown>) => {
116
+ const props = instance?.vnode.props as Record<string, unknown> | undefined;
117
+ const collect = (value: unknown): SubmitListener[] =>
118
+ Array.isArray(value)
119
+ ? (value as SubmitListener[])
120
+ : typeof value === 'function'
121
+ ? [value as SubmitListener]
122
+ : [];
123
+
124
+ const listeners = collect(props?.onSubmit);
125
+ if (!onceFired) {
126
+ const once = collect(props?.onSubmitOnce);
127
+ if (once.length) {
128
+ onceFired = true;
129
+ listeners.push(...once);
130
+ }
131
+ }
132
+ if (!listeners.length) return undefined;
133
+
134
+ const pending: Array<PromiseLike<unknown>> = [];
135
+ for (const listener of listeners) {
136
+ try {
137
+ const result = listener(formValues);
138
+ if (isThenable(result)) {
139
+ pending.push(Promise.resolve(result).catch(reportListenerError));
140
+ }
141
+ } catch (error) {
142
+ reportListenerError(error);
143
+ }
144
+ }
145
+ // Await only what is actually awaitable, so a sync handler keeps the exact
146
+ // timing it has today rather than being pushed a microtask later.
147
+ return pending.length ? Promise.all(pending) : undefined;
148
+ };
149
+
150
+ const onSubmit = handleSubmit((formValues) => runSubmitListeners(formValues));
63
151
 
64
152
  /**
65
153
  * The rendered `<form>`, exposed as `$el` the way `BbButton` does it.