bitboss-ui 3.0.0-beta.45 → 3.0.0-beta.47

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 (184) hide show
  1. package/README.md +2 -2
  2. package/bin/bitboss-ui-mcp.mjs +5 -1
  3. package/bin/bitboss-ui.mjs +20 -7
  4. package/dist/ai/BbAccordion.md +24 -2
  5. package/dist/ai/BbCalendar.md +13 -2
  6. package/dist/ai/BbColorPalette.md +1 -1
  7. package/dist/ai/BbDatePicker.md +2 -2
  8. package/dist/ai/BbDatePickerInput.md +5 -3
  9. package/dist/ai/BbDialog.md +13 -11
  10. package/dist/ai/BbDropdown.md +13 -1
  11. package/dist/ai/BbDropzone.md +2 -2
  12. package/dist/ai/BbForm.md +2 -2
  13. package/dist/ai/BbHoverCard.md +515 -0
  14. package/dist/ai/BbHoverCardPortal.md +29 -0
  15. package/dist/ai/BbOffCanvas.md +4 -4
  16. package/dist/ai/BbPopover.md +127 -4
  17. package/dist/ai/BbPopoverPortal.md +29 -0
  18. package/dist/ai/BbRating.md +1 -1
  19. package/dist/ai/BbSelectPopover.md +1 -1
  20. package/dist/ai/BbTable.md +18 -10
  21. package/dist/ai/BbTabs.md +43 -5
  22. package/dist/ai/BbTabsRoot.md +1 -0
  23. package/dist/ai/BbTextInput.md +13 -11
  24. package/dist/ai/BbTextarea.md +8 -7
  25. package/dist/ai/BbTooltip.md +22 -6
  26. package/dist/ai/changelog.json +58 -3
  27. package/dist/ai/components.json +30 -15
  28. package/dist/ai/guides/ai-router.md +23 -22
  29. package/dist/ai/guides/component-picker.md +17 -11
  30. package/dist/ai/guides/installation-and-plugin-setup.md +40 -11
  31. package/dist/ai/guides/migration/components/bb-date-picker-input.md +5 -2
  32. package/dist/ai/guides/migration/v2-to-v3.md +49 -3
  33. package/dist/ai/guides/passthrough.md +5 -6
  34. package/dist/ai/guides/typescript-generic-components.md +87 -0
  35. package/dist/ai/guides/validation-libraries.md +7 -4
  36. package/dist/ai/index.md +5 -1
  37. package/dist/ai/manifest/components/BbAccordion.json +1 -0
  38. package/dist/ai/manifest/components/BbAccordion.tree.json +1 -0
  39. package/dist/ai/manifest/components/BbCalendar.json +1 -1
  40. package/dist/ai/manifest/components/BbDatePicker.json +1 -1
  41. package/dist/ai/manifest/components/BbDatePickerInput.json +1 -1
  42. package/dist/ai/manifest/components/BbHoverCard.json +63 -0
  43. package/dist/ai/manifest/components/BbHoverCard.tree.json +31 -0
  44. package/dist/ai/manifest/components/BbHoverCardPortal.json +21 -0
  45. package/dist/ai/manifest/components/BbPopover.json +1 -1
  46. package/dist/ai/manifest/components/BbPopoverPortal.json +21 -0
  47. package/dist/ai/manifest/components/BbTable.json +1 -1
  48. package/dist/ai/manifest/components/BbTabs.json +1 -0
  49. package/dist/ai/manifest/components/BbTabsRoot.json +1 -0
  50. package/dist/ai/manifest/components/BbTextInput.json +4 -3
  51. package/dist/ai/manifest/components/BbTextarea.json +2 -1
  52. package/dist/ai/manifest/index.json +6 -3
  53. package/dist/ai/manifest/meta.json +12 -0
  54. package/dist/ai/manifest/types.advanced.json +6 -0
  55. package/dist/ai/manifest/types.api.json +4 -3
  56. package/dist/ai/manifest/types.wrapper.json +3 -0
  57. package/dist/ai/recipes/inertia/approvals-inbox.md +8 -6
  58. package/dist/ai/recipes/nuxt/approvals-inbox.md +8 -6
  59. package/dist/ai/recipes/vue/approvals-inbox.md +8 -6
  60. package/dist/ai/source/BbAccordion.md +44 -12
  61. package/dist/ai/source/BbCalendar.md +7 -4
  62. package/dist/ai/source/BbDatePicker.md +4 -3
  63. package/dist/ai/source/BbDatePickerInput.md +6 -2
  64. package/dist/ai/source/BbDialog.md +5 -2
  65. package/dist/ai/source/BbDropdown.md +43 -16
  66. package/dist/ai/source/BbDropzone.md +3 -2
  67. package/dist/ai/source/BbHoverCard.md +948 -0
  68. package/dist/ai/source/BbHoverCardPortal.md +299 -0
  69. package/dist/ai/source/BbOffCanvas.md +5 -2
  70. package/dist/ai/source/BbPopover.md +29 -15
  71. package/dist/ai/source/BbPopoverPortal.md +360 -0
  72. package/dist/ai/source/BbRating.md +1 -1
  73. package/dist/ai/source/BbSelect.md +4 -2
  74. package/dist/ai/source/BbSelectPopover.md +10 -2
  75. package/dist/ai/source/BbSlider.md +4 -3
  76. package/dist/ai/source/BbTable.md +91 -12
  77. package/dist/ai/source/BbTabs.md +22 -0
  78. package/dist/ai/source/BbTabsList.md +16 -0
  79. package/dist/ai/source/BbTabsPanels.md +16 -0
  80. package/dist/ai/source/BbTabsRoot.md +22 -0
  81. package/dist/ai/source/BbTextInput.md +44 -12
  82. package/dist/ai/source/BbTextarea.md +45 -13
  83. package/dist/ai/source/BbTimePickerInput.md +2 -1
  84. package/dist/ai/source/BbTooltip.md +38 -5
  85. package/dist/ai/source/CommonPopover.md +16 -2
  86. package/dist/components/BbAccordion/BbAccordion.vue_vue_type_script_setup_true_lang.js +33 -29
  87. package/dist/components/BbAccordion/types.d.ts +8 -0
  88. package/dist/components/BbCalendar/BbCalendar.vue.d.ts +0 -1
  89. package/dist/components/BbCalendar/BbCalendar.vue_vue_type_script_setup_true_lang.js +182 -181
  90. package/dist/components/BbCalendar/types.d.ts +4 -2
  91. package/dist/components/BbCalendar/useCalendarGrid.d.ts +5 -2
  92. package/dist/components/BbCalendar/useCalendarGrid.js +119 -119
  93. package/dist/components/BbCalendar/useDayGrid.d.ts +5 -2
  94. package/dist/components/BbCalendar/useDayGrid.js +89 -89
  95. package/dist/components/BbCalendar/useDayjsLocale.d.ts +10 -0
  96. package/dist/components/BbCalendar/useDayjsLocale.js +7 -7
  97. package/dist/components/BbDatePicker/BbDatePicker.vue.d.ts +0 -1
  98. package/dist/components/BbDatePicker/BbDatePicker.vue_vue_type_script_setup_true_lang.js +1 -1
  99. package/dist/components/BbDatePicker/types.d.ts +4 -2
  100. package/dist/components/BbDatePickerInput/BbDatePickerInput.vue_vue_type_script_setup_true_lang.js +2 -1
  101. package/dist/components/BbDatePickerInput/types.d.ts +4 -1
  102. package/dist/components/BbDialog/BbDialog.vue_vue_type_script_setup_true_lang.js +2 -1
  103. package/dist/components/BbDropdown/BbDropdown.vue_vue_type_script_setup_true_lang.js +347 -338
  104. package/dist/components/BbDropzone/BbDropzone.vue_vue_type_script_setup_true_lang.js +1 -1
  105. package/dist/components/BbHoverCard/BbHoverCard.vue.d.ts +43 -0
  106. package/dist/components/BbHoverCard/BbHoverCard.vue.js +6 -0
  107. package/dist/components/BbHoverCard/BbHoverCard.vue_vue_type_script_setup_true_lang.js +297 -0
  108. package/dist/components/BbHoverCard/BbHoverCardPortal.vue.d.ts +23 -0
  109. package/dist/components/BbHoverCard/BbHoverCardPortal.vue.js +5 -0
  110. package/dist/components/BbHoverCard/BbHoverCardPortal.vue_vue_type_script_setup_true_lang.js +13 -0
  111. package/dist/components/BbHoverCard/hoverCardFocus.d.ts +29 -0
  112. package/dist/components/BbHoverCard/hoverCardFocus.js +29 -0
  113. package/dist/components/BbHoverCard/types.d.ts +175 -0
  114. package/dist/components/BbHoverCard/types.js +13 -0
  115. package/dist/components/BbOffCanvas/BbOffCanvas.vue_vue_type_script_setup_true_lang.js +2 -1
  116. package/dist/components/BbPopover/BbPopover.vue_vue_type_script_setup_true_lang.js +152 -145
  117. package/dist/components/BbPopover/BbPopoverPortal.vue.d.ts +23 -0
  118. package/dist/components/BbPopover/BbPopoverPortal.vue.js +5 -0
  119. package/dist/components/BbPopover/BbPopoverPortal.vue_vue_type_script_setup_true_lang.js +13 -0
  120. package/dist/components/BbRating/BbRating.vue_vue_type_script_setup_true_lang.js +1 -1
  121. package/dist/components/BbSelect/BbSelect.vue_vue_type_script_setup_true_lang.js +1 -1
  122. package/dist/components/BbSelectPopover/BbSelectPopover.vue_vue_type_script_setup_true_lang.js +4 -1
  123. package/dist/components/BbSlider/BbSlider.vue_vue_type_script_setup_true_lang.js +2 -2
  124. package/dist/components/BbTable/BbTable.vue_vue_type_script_setup_true_lang.js +1003 -974
  125. package/dist/components/BbTable/BbTableDataRow.d.ts +2 -0
  126. package/dist/components/BbTable/BbTableDataRow.js +1 -1
  127. package/dist/components/BbTable/BbTableSelectToggle.d.ts +12 -4
  128. package/dist/components/BbTable/BbTableSelectToggle.js +19 -10
  129. package/dist/components/BbTable/types.d.ts +1 -1
  130. package/dist/components/BbTabs/BbTabs.vue_vue_type_script_setup_true_lang.js +4 -0
  131. package/dist/components/BbTabs/BbTabsRoot.vue_vue_type_script_setup_true_lang.js +4 -0
  132. package/dist/components/BbTabs/types.d.ts +16 -0
  133. package/dist/components/BbTextInput/BbTextInput.vue.d.ts +31 -39
  134. package/dist/components/BbTextInput/BbTextInput.vue_vue_type_script_setup_true_lang.js +19 -18
  135. package/dist/components/BbTextInput/types.d.ts +17 -4
  136. package/dist/components/BbTextarea/BbTextarea.vue.d.ts +31 -37
  137. package/dist/components/BbTextarea/BbTextarea.vue_vue_type_script_setup_true_lang.js +40 -39
  138. package/dist/components/BbTextarea/types.d.ts +17 -4
  139. package/dist/components/BbTimePickerInput/BbTimePickerInput.vue_vue_type_script_setup_true_lang.js +2 -1
  140. package/dist/components/BbTooltip/BbTooltip.vue_vue_type_script_setup_true_lang.js +86 -81
  141. package/dist/components/CommonPopover/CommonPopover.vue_vue_type_script_setup_true_lang.js +18 -16
  142. package/dist/composables/useBbTabsContext.js +47 -36
  143. package/dist/composables/useOverlayYield.d.ts +23 -0
  144. package/dist/composables/useOverlayYield.js +20 -0
  145. package/dist/composables/useSegmentedFields.js +4 -4
  146. package/dist/directives/bbDropdown.js +22 -12
  147. package/dist/directives/bbHoverCard.d.ts +59 -0
  148. package/dist/directives/bbHoverCard.js +33 -0
  149. package/dist/directives/bbPopover.d.ts +60 -0
  150. package/dist/directives/bbPopover.js +40 -0
  151. package/dist/directives/bbTooltip.d.ts +7 -5
  152. package/dist/directives/createPopoverDirective.d.ts +24 -0
  153. package/dist/directives/createPopoverDirective.js +74 -61
  154. package/dist/directives/directivePortal.d.ts +69 -0
  155. package/dist/directives/directivePortal.js +40 -0
  156. package/dist/directives/renderingInstance.d.ts +2 -0
  157. package/dist/directives/renderingInstance.js +25 -0
  158. package/dist/index.d.ts +10 -0
  159. package/dist/index.js +61 -56
  160. package/dist/llms-full.txt +1167 -153
  161. package/dist/llms-medium.txt +84 -45
  162. package/dist/llms.txt +2 -1
  163. package/dist/nuxt-auto-imports.js +1 -1
  164. package/dist/plugin.js +18 -16
  165. package/dist/project-dts.d.ts +8 -0
  166. package/dist/project-dts.js +6 -3
  167. package/dist/runtime/nuxt-plugin.js +19 -17
  168. package/dist/styles.css +2 -2
  169. package/dist/types/Config.d.ts +26 -8
  170. package/dist/types/ptComponentMap.d.ts +1 -0
  171. package/dist/utils/overlayEscapeStack.d.ts +30 -2
  172. package/dist/utils/overlayEscapeStack.js +19 -10
  173. package/dist/utils/versionCheck.js +1 -1
  174. package/dist/vite-plugin.d.ts +6 -4
  175. package/dist/vite.js +182 -181
  176. package/llms.txt +2 -1
  177. package/package.json +1 -1
  178. package/scripts/lib/eslint-disable.mjs +4 -0
  179. package/scripts/lib/eslint-plugin.d.ts +4 -0
  180. package/scripts/lib/eslint-plugin.mjs +240 -6
  181. package/scripts/lib/text-null-value.mjs +260 -0
  182. package/scripts/lib/toolchain-check.mjs +12 -19
  183. package/scripts/lib/unimported-components.mjs +260 -0
  184. package/scripts/lib/validate-bb-markup.mjs +94 -1
package/README.md CHANGED
@@ -57,7 +57,7 @@ Full detail: `dist/ai/guides/installation-and-plugin-setup.md`.
57
57
 
58
58
  ### Directives
59
59
 
60
- `vBbTooltip`, `vBbDropdown`, `vBbColor`, `vBbDate`, `vBbTime` — plus a
60
+ `vBbTooltip`, `vBbDropdown`, `vBbColor`, `vBbDate`, `vBbTime`, `vBbHoverCard`, `vBbPopover` — plus a
61
61
  `Bb*DirectivePlugin` for each, for app-level registration. Prefer the directive
62
62
  over the component for simple no-slot cases.
63
63
 
@@ -79,7 +79,7 @@ Product-ready components with labels, hints, errors, and consistent styling:
79
79
  - **Forms & actions:** `BbTextInput`, `BbTextarea`, `BbNumberInput`, `BbSelect`, `BbSelectPopover`, `BbCheckbox`, `BbCheckboxGroup`, `BbRadio`, `BbRadioGroup`, `BbSwitch`, `BbSwitchGroup`, `BbSlider`, `BbRating`, `BbColorInput`, `BbColorPalette`, `BbCalendar`, `BbDatePicker`, `BbDatePickerInput`, `BbTimePicker`, `BbTimePickerInput`, `BbButton`, `BbBadge`, `BbBadgeButton`, `BbIndicator`, `BbTag`, `BbDropdown`, `BbDropdownGroup`, `BbDropdownButton`, `BbAsterisk`
80
80
  - **Feedback:** `BbAlert`, `BbProgress`, `BbSpinner`, `BbToast`, `BbToastPortal`, `BbTooltip`
81
81
  - **Layout & navigation:** `BbAccordion`, `BbCollapsible`, `BbTabs` (+ `BbTabsRoot`, `BbTabsList`, `BbTabsPanels`), `BbBreadcrumbs`, `BbPagination`, `BbSmoothHeight`
82
- - **Overlays & panels:** `BbDialog`, `BbConfirm`, `BbConfirmPortal`, `BbOffCanvas`, `BbPopover`
82
+ - **Overlays & panels:** `BbDialog`, `BbConfirm`, `BbConfirmPortal`, `BbOffCanvas`, `BbPopover`, `BbHoverCard`
83
83
  - **Data:** `BbTable`, `BbTree`
84
84
  - **Media & files:** `BbAvatar`, `BbDropzone`, `BbIcon`
85
85
  - **Forms** (`BbForm` needs the `vee-validate` peer; an app without forms needs nothing): `BbForm`, `useBbFormContext`, and the 16 form controls join the form they are inside (plus `rules` / `validation-label` / `validation-mode` / `formless`; the field's path is its `name`, else its label).
@@ -1188,7 +1188,11 @@ function getComponent({
1188
1188
  // A directive (`v-bb-tooltip`, `vBbTooltip`) — review D3-13.
1189
1189
  const directiveName = String(name ?? '')
1190
1190
  .trim()
1191
- .replace(/^vBb([A-Z]\w*)$/, (_, word) => `v-bb-${word.toLowerCase()}`)
1191
+ .replace(
1192
+ /^vBb([A-Z]\w*)$/,
1193
+ // `vBbHoverCard` → `v-bb-hover-card`, the registered name.
1194
+ (_, word) => `v-bb-${word.replace(/(?<!^)([A-Z])/g, '-$1')}`
1195
+ )
1192
1196
  .toLowerCase();
1193
1197
  const directive = (manifest.directives ?? []).find(
1194
1198
  (d) => d.name === directiveName
@@ -76,6 +76,8 @@ import {
76
76
  projectLocalBbComponents,
77
77
  } from '../scripts/lib/local-components.mjs';
78
78
  import { applyBitbossFixes } from '../scripts/lib/check-fix.mjs';
79
+ import { stringControlsFor } from '../scripts/lib/text-null-value.mjs';
80
+ import { projectResolution } from '../scripts/lib/unimported-components.mjs';
79
81
  import { cssClassFindings } from '../scripts/lib/css-class-check.mjs';
80
82
  import {
81
83
  cssLocalFindings,
@@ -1044,7 +1046,13 @@ async function checkCommand(
1044
1046
  const content = readText(file);
1045
1047
  const { findings: fileFindings } = file.endsWith('.md')
1046
1048
  ? validateMarkdown(content, manifest, validateOptions)
1047
- : validateVueSnippet(content, manifest, validateOptions);
1049
+ : validateVueSnippet(content, manifest, {
1050
+ ...validateOptions,
1051
+ // The app's null-value policy, from the project bitboss-ui.d.ts.
1052
+ stringControls: stringControlsFor(file),
1053
+ // Q61.19: what the project registers globally.
1054
+ unimported: { project: projectResolution(projectRoot) },
1055
+ });
1048
1056
  // Q46.9: a finding the author silenced for ESLint (`eslint-disable*`,
1049
1057
  // usually with a reason) is silenced here too, or `check` cannot gate
1050
1058
  // CI next to a lint step that passes. `.md` lines are fence-relative,
@@ -1076,8 +1084,9 @@ async function checkCommand(
1076
1084
  }
1077
1085
 
1078
1086
  for (const [name, file] of projectLocal) warnLocal(name, file);
1079
- // Q60.6: a `vue-tsc` below 3 and a missing `checkUnknownComponents`, read
1080
- // from package.json and the tsconfig. One line each, never fatal.
1087
+ // Q60.6: a `vue-tsc` below 3, read from package.json. One line, never
1088
+ // fatal. (A missing `checkUnknownComponents` is no longer warned about:
1089
+ // `unimported-component` covers it, Q61.19.)
1081
1090
  warnings.push(...toolchainWarnings(projectRoot));
1082
1091
 
1083
1092
  // Q25: a hand import of `bitboss-ui/styles.css` while the runtime plugin
@@ -1388,12 +1397,19 @@ Commands:
1388
1397
  \`duplicate-field-identity\` (two validated fields in
1389
1398
  one \`BbForm\` on the same path: the same \`name\`,
1390
1399
  or the same label with no \`name\`),
1400
+ \`unimported-component\` (a \`.vue\` file uses a
1401
+ bitboss-ui tag it never imports and the project never
1402
+ registers globally: it renders as an empty element),
1391
1403
  \`reactive-v-model\` (a \`v-model\` on a \`<script
1392
1404
  setup>\` \`const x = reactive(…)\`: a production build
1393
1405
  rejects the write-back; hold it in a \`ref()\`),
1394
1406
  \`form-v-model\` (\`v-model\`, \`:model-value\` or an
1395
1407
  \`@update:model-value\` listener on a \`BbForm\`: it
1396
1408
  takes its object one way, \`:model="draft"\`),
1409
+ \`bare-portal-name\` (\`v-bb-popover="user-card"\` /
1410
+ \`v-bb-hover-card="user-card"\` without the inner
1411
+ quotes: an expression, not a portal name; write
1412
+ \`"'user-card'"\`),
1397
1413
  \`positional-provider\` (an \`items\` provider with
1398
1414
  positional arguments: it receives one object,
1399
1415
  \`({ query, reason, modelValue, signal })\`),
@@ -1456,10 +1472,7 @@ Commands:
1456
1472
  \`reason\` against one cause other than \`'search'\`;
1457
1473
  check \`reason !== 'search'\`), \`vue-tsc-major\`
1458
1474
  (\`vue-tsc\` below 3 in package.json: the generic
1459
- components' props go unchecked),
1460
- \`check-unknown-components\` (no
1461
- \`vueCompilerOptions.checkUnknownComponents: true\` in
1462
- the tsconfig; \`ai-init\` writes it).
1475
+ components' props go unchecked).
1463
1476
  changelog [--since <version>] [--until <version>] [--json]
1464
1477
  Breaking changes between two releases — "what breaks if I
1465
1478
  bump". \`--since\` is the version you are ON, and is
@@ -39,7 +39,8 @@ Step down the ladder when the composed header doesn't fit:
39
39
  **`BbSmoothHeight`** (`./BbSmoothHeight.md`).
40
40
 
41
41
  The public surface is small on purpose: `v-model` (boolean), `eager`,
42
- `transition-duration`, `id`, two slots, and the `update:modelValue` event.
42
+ `transition-duration`, `id`, `heading-level`, two slots, and the
43
+ `update:modelValue` event.
43
44
  Everything else is composition.
44
45
 
45
46
  ### Composing an FAQ
@@ -489,6 +490,25 @@ references): the body region gets your id and the header gets `<id>_header`,
489
490
  already cross-wired via `aria-controls`/`aria-labelledby`. Omitted, a unique id
490
491
  is generated for you.
491
492
 
493
+ ### Headers in the page outline
494
+
495
+ Set `heading-level` when the accordion's headers are section titles a screen
496
+ reader user should find by jumping between headings: an FAQ, a settings page's
497
+ sections. The header button then sits inside `<h1>`–`<h6>` (the APG accordion
498
+ shape), named after your header text. Match the page outline: under an `<h2>`
499
+ the panels take `3`, never a level picked for its size, because the heading
500
+ looks the same at every level (its margins and font are reset). Leave it out
501
+ for accordions inside a form, a card or a side panel, where the headers are
502
+ controls, not titles.
503
+
504
+ ```vue
505
+ <h2>Billing questions</h2>
506
+ <BbAccordion v-model="open" :heading-level="3">
507
+ <template #header>Can I change plans?</template>
508
+ Yes, at any time.
509
+ </BbAccordion>
510
+ ```
511
+
492
512
  ### Restyling with passthrough
493
513
 
494
514
  `pt` reaches a named part with a class list — or, in the object form, a
@@ -606,7 +626,8 @@ A `pt` value written in `<script>` (a constant, a preset, a wrapper's
606
626
  - The header is a real `button` with `aria-expanded`/`aria-controls`; the body
607
627
  is a `role="region"` labelled by the header via `aria-labelledby`. You add
608
628
  nothing — and you must not wrap the header slot in another interactive
609
- element.
629
+ element. `heading-level` puts the button inside a heading (above); never wrap
630
+ the accordion in your own `<hN>` instead.
610
631
  - Keyboard comes free with the native button: Tab to the header, Enter/Space
611
632
  toggles. Closed content is `inert`, so it can never be tabbed into.
612
633
  - Provide a visible, meaningful header label; the chevron is decorative (mark
@@ -623,6 +644,7 @@ A `pt` value written in `<script>` (a constant, a preset, a wrapper's
623
644
  | --- | --- | --- | --- | --- |
624
645
  | `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: `header`, `panel`, `root`. States: `open`. 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 `BbAccordionProps['pt']` (`PtValue` is not exported): § Typing `pt` in `<script>`. |
625
646
  | `eager` | `boolean \| undefined` | `false` | | Prevents rendering content before it's made visible. |
647
+ | `headingLevel` | `1 \| 2 \| 3 \| 4 \| 5 \| 6 \| undefined` | | | Wraps the header button in a heading of this level (`<h1>`–`<h6>`), the APG accordion shape, so the header joins the page's outline. Match the level to where the accordion sits (a section under an `<h2>` takes `3`). No visual change: the heading's margins and font are reset. Omitted, there is no heading. |
626
648
  | `id` | `string \| undefined` | | | The root's `id`; the header button's id (`<id>_header`) derives from it. Generated when omitted. |
627
649
  | `modelValue` | `boolean \| undefined` | `false` | | Used by v-model to trigger opening / closing the collapsible. An accordion with no `v-model` starts closed. |
628
650
  | `transitionDuration` | `number \| undefined` | `250` | | How long the transition has to last in milliseconds |
@@ -493,11 +493,22 @@ const isOpen = (date: string) => !closed.has(date);
493
493
  </style>
494
494
  ```
495
495
 
496
+ ### The first day of the week
497
+
498
+ The grid's week starts on the active locale's day, from the dayjs pack the
499
+ plugin bundled: Monday for `it`, `de`, `fr`, `es` or `en-gb`, Sunday for bare
500
+ `en` (US English) and `pt` (Brazil), and Sunday for a locale with no pack. The
501
+ weekday headers follow, and so does a locale switch at runtime. Pass
502
+ `first-day-of-week` (`0` = Sunday … `6` = Saturday) when the app's week is not
503
+ its language's — an `en` app for a European audience takes
504
+ `:first-day-of-week="1"`. `BbDatePicker` and `BbDatePickerInput` take the same
505
+ prop and the same default.
506
+
496
507
  ### Keyboard and accessibility
497
508
 
498
509
  The calendar is an ordinary control in the tab order: **one tab stop** lands
499
510
  on the day under the cursor (the value, or today), arrows move by day and
500
- week, Home / End jump to the week's ends, PageUp / PageDown page by month
511
+ week, Home / End jump to the ends of the grid's row, PageUp / PageDown page by month
501
512
  (with Shift, by year), typing a day number jumps to it, Enter or Space picks.
502
513
  The grid is a `role="grid"` of `role="row"` / `gridcell` nodes; a picked day
503
514
  carries `aria-selected` and says "selected" in its name, a pending range
@@ -580,7 +591,7 @@ the page is `BbDatePicker`.
580
591
  | `activeSegment` | `'start'` \| `'end'` | | | Which end the time rail edits in range + `type="datetime"` (`v-model:active-segment`). Standalone use manages this internally; an embedding host (the date input) drives it from its focused field. Ignored outside range + datetime. |
581
592
  | `ampm` | `boolean \| undefined` | `false` | | 12-hour display with an AM/PM column (requires `type="datetime"`); emits stay 24h. |
582
593
  | `disabled` | `boolean \| undefined` | `false` | | Disables every cell, the navigation and the time rail. |
583
- | `firstDayOfWeek` | `0 \| 1 \| 2 \| 3 \| 4 \| 5 \| 6 \| undefined` | `1` | | First day of the week (0 = Sunday … 6 = Saturday). |
594
+ | `firstDayOfWeek` | `0 \| 1 \| 2 \| 3 \| 4 \| 5 \| 6 \| undefined` | `the locale's week start` | | First day of the week (0 = Sunday … 6 = Saturday). Omitted, the active locale's week start: Monday for `it`, `de`, `fr`, `en-gb`…, Sunday for bare `en` (US) and `pt` (Brazil). |
584
595
  | `floating` | `boolean \| undefined` | `false` | | Emit plain calendar strings instead of zoned ISO instants. Implied (and forced) by `type="month"` and `type="year"`. |
585
596
  | `max` | `string \| undefined` | | | Maximum selectable value, in this `type`'s shape. A finer bound is accepted and ceiled to the unit, with a warning. With `datetime`, on its own day the time rail disables the times after it. |
586
597
  | `min` | `string \| undefined` | | | Minimum selectable value, in this `type`'s shape — `YYYY-MM-DD` (`YYYY-MM-DDTHH:mm` with `datetime`, `YYYY-MM` with `month`, `YYYY` with `year`). A finer bound is accepted and floored to the unit, with a warning. With `datetime`, the grid disables the days before it and, on its own day, the time rail disables the times before it. |
@@ -827,4 +827,4 @@ Set these on the element, or on a class you put on it, to retune this component
827
827
  ## See Also
828
828
 
829
829
  - [BbColorInput](./BbColorInput.md) — Captures color values through input controls.
830
- - [BbPopover](./BbPopover.md) — Anchors floating content to reference elements.
830
+ - [BbPopover](./BbPopover.md) — Anchors floating content to reference elements; also the v-bb-popover directive (its body is a BbPopoverPortal or a component).
@@ -420,7 +420,7 @@ and digit typeahead; the time rail exposes labelled columns. Always pass a
420
420
  | `disabled` | `boolean \| undefined` | `false` | | Disables the activator and the calendar. |
421
421
  | `disableFlip` | `boolean \| undefined` | `false` | | Disable the automatic flip to the opposite side on overflow. |
422
422
  | `eager` | `boolean \| undefined` | `false` | | Render popover content before it is first shown. |
423
- | `firstDayOfWeek` | `0 \| 1 \| 2 \| 3 \| 4 \| 5 \| 6 \| undefined` | `1` | | First day of the week (0 = Sunday … 6 = Saturday). |
423
+ | `firstDayOfWeek` | `0 \| 1 \| 2 \| 3 \| 4 \| 5 \| 6 \| undefined` | `the locale's week start` | | First day of the week (0 = Sunday … 6 = Saturday). Omitted, the active locale's week start: Monday for `it`, `de`, `fr`, `en-gb`…, Sunday for bare `en` (US) and `pt` (Brazil). |
424
424
  | `floating` | `boolean \| undefined` | `false` | | Emit plain calendar strings instead of zoned ISO instants. Implied (and forced) by `type="month"` and `type="year"`. |
425
425
  | `label` | `string \| undefined` | | | Accessible label. The activator is named by it followed by the activator's own text ("Due date 12 Sep"), through `aria-labelledby`. |
426
426
  | `max` | `string \| undefined` | | | Maximum selectable value, in this `type`'s shape. A finer bound is accepted and ceiled to the unit, with a warning. |
@@ -542,4 +542,4 @@ Set these on the element, or on a class you put on it, to retune this component
542
542
  - [BbCalendar](./BbCalendar.md) — A calendar in place — the month grid (or month / year grid, or day grid plus time rail) with no popover and no field chrome; v-model of a day, range, set, month, year or datetime.
543
543
  - [BbDatePickerInput](./BbDatePickerInput.md) — Combines date selection with text input behavior.
544
544
  - [BbColorPalette](./BbColorPalette.md) — Opens an anchored popover color palette for color picking interactions.
545
- - [BbPopover](./BbPopover.md) — Anchors floating content to reference elements.
545
+ - [BbPopover](./BbPopover.md) — Anchors floating content to reference elements; also the v-bb-popover directive (its body is a BbPopoverPortal or a component).
@@ -213,8 +213,10 @@ to catch config mistakes early — always pass the `YYYY-MM-DD` shape.
213
213
 
214
214
  For non-contiguous rules (no weekends, no holidays, only in-stock slots), pass a
215
215
  `selectable` predicate: it receives each date string and returns `false` to
216
- disable that day. `firstDayOfWeek` shifts the calendar grid (`0` = Sunday …
217
- `6` = Saturday).
216
+ disable that day. The grid's week starts on the locale's day (Monday for
217
+ `it` or `de`, Sunday for bare `en` and `pt`); `firstDayOfWeek` pins it
218
+ (`0` = Sunday … `6` = Saturday) when the app's week differs from its
219
+ language's.
218
220
 
219
221
  The example below combines a rolling `min`/`max` window, a weekdays-only
220
222
  predicate, `floating` output, and the `day:append` slot to dot specific days.
@@ -872,7 +874,7 @@ label>')` matches nothing; query each segment by its own label instead. See
872
874
  | `disabled` | `boolean \| undefined` | `false` | | Disables the component. |
873
875
  | `disableWriting` | `boolean \| "mobile" \| "desktop" \| undefined` | `false` | | Disables typing into the input. Use `'mobile'` to disable typing only on mobile, `'desktop'` to disable typing only on desktop. |
874
876
  | `errors` | `string \| string[] \| undefined` | | | Can be a string or an array of string containing the messages to display. They render in an `aria-live="polite"` region (announced when they appear) and, while the list is non-empty, are referenced FIRST from the control's `aria-describedby`, so the reason the field is invalid is re-read whenever the control regains focus. |
875
- | `firstDayOfWeek` | `0 \| 1 \| 2 \| 3 \| 4 \| 5 \| 6 \| undefined` | | | Defines the first day of the week with `0` meaning Sunday and `6` meaning Saturday. |
877
+ | `firstDayOfWeek` | `0 \| 1 \| 2 \| 3 \| 4 \| 5 \| 6 \| undefined` | `the locale's week start` | | First day of the week (0 = Sunday … 6 = Saturday). Omitted, the active locale's week start: Monday for `it`, `de`, `fr`, `en-gb`…, Sunday for bare `en` (US) and `pt` (Brazil). |
876
878
  | `floating` | `boolean \| undefined` | `false` | | If true the date will have a format YYYY-MM-DD instead of the default ISO string. |
877
879
  | `formless` | `boolean \| undefined` | `false` | | Keep this control out of the enclosing `BbForm` entirely: not submitted, not dirty, not reset, not validated. For a control that is not form data (a filter, a "show advanced" switch). |
878
880
  | `hasErrors` | `boolean \| undefined` | `false` | | Define if the component should be in an error state. It usually attaches a CSS class for styling purposes. |
@@ -99,7 +99,7 @@ back to `false`, which is the same thing.
99
99
  <template #footer>
100
100
  <div class="flex justify-end gap-2">
101
101
  <BbButton variant="ghost" @click="open = false">Cancel</BbButton>
102
- <BbButton :disabled="!name.trim()" variant="primary" @click="create">
102
+ <BbButton :disabled="!name?.trim()" variant="primary" @click="create">
103
103
  Create
104
104
  </BbButton>
105
105
  </div>
@@ -112,7 +112,7 @@ import { ref } from 'vue';
112
112
  import { BbButton, BbDialog, BbTextInput } from 'bitboss-ui';
113
113
 
114
114
  const open = ref(false);
115
- const name = ref('');
115
+ const name = ref<string | null>('');
116
116
 
117
117
  // Re-seed on `@show`, not on close — a dismissed dialog may be reopened.
118
118
  const reset = () => {
@@ -442,7 +442,7 @@ const roles = [
442
442
  ];
443
443
 
444
444
  const open = ref(false);
445
- const email = ref('');
445
+ const email = ref<string | null>('');
446
446
  const role = ref('member');
447
447
 
448
448
  // Re-seed on `@show`, not on close — a dismissed dialog may be reopened.
@@ -452,7 +452,9 @@ const reset = () => {
452
452
  };
453
453
 
454
454
  // Primary action stays disabled until the email is plausible.
455
- const canSend = computed(() => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email.value));
455
+ const canSend = computed(() =>
456
+ /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email.value ?? '')
457
+ );
456
458
 
457
459
  const send = () => {
458
460
  // Send the invite here, then close only on success.
@@ -600,7 +602,7 @@ element.
600
602
  <div class="flex justify-end gap-2">
601
603
  <!-- Keep a visible way out — never trap the user. -->
602
604
  <BbButton variant="ghost" @click="open = false">Skip</BbButton>
603
- <BbButton :disabled="!company.trim()" variant="primary" @click="save">
605
+ <BbButton :disabled="!company?.trim()" variant="primary" @click="save">
604
606
  Save
605
607
  </BbButton>
606
608
  </div>
@@ -613,8 +615,8 @@ import { ref } from 'vue';
613
615
  import { BbButton, BbDialog, BbTextInput } from 'bitboss-ui';
614
616
 
615
617
  const open = ref(false);
616
- const displayName = ref('');
617
- const company = ref('');
618
+ const displayName = ref<string | null>('');
619
+ const company = ref<string | null>('');
618
620
 
619
621
  // Re-seed on `@show` so a dismissed step reopens clean.
620
622
  const reset = () => {
@@ -676,7 +678,7 @@ automatically; close only once the work resolves:
676
678
  Cancel
677
679
  </BbButton>
678
680
  <!-- Returning the promise lets BbButton run its own loading state. -->
679
- <BbButton :disabled="!draft.trim()" variant="primary" @click="save">
681
+ <BbButton :disabled="!draft?.trim()" variant="primary" @click="save">
680
682
  Rename
681
683
  </BbButton>
682
684
  </div>
@@ -692,7 +694,7 @@ import { BbButton, BbDialog, BbTextInput } from 'bitboss-ui';
692
694
  const projectName = ref('acme-storefront');
693
695
 
694
696
  const open = ref(false);
695
- const draft = ref('');
697
+ const draft = ref<string | null>('');
696
698
  const saving = ref(false);
697
699
 
698
700
  // Re-seed on `@show` so a dismissed rename never resurfaces half-typed.
@@ -706,7 +708,7 @@ const save = async () => {
706
708
  try {
707
709
  // Simulated request — replace with your API call.
708
710
  await new Promise((resolve) => setTimeout(resolve, 600));
709
- projectName.value = draft.value.trim();
711
+ projectName.value = (draft.value ?? '').trim();
710
712
  // Work first, then close: the transition runs while your UI refreshes.
711
713
  open.value = false;
712
714
  } finally {
@@ -1212,4 +1214,4 @@ Set these on the element, or on a class you put on it, to retune this component
1212
1214
 
1213
1215
  - [BbOffCanvas](./BbOffCanvas.md) — Shows side-panel overlay content outside normal layout flow.
1214
1216
  - [BbConfirm](./BbConfirm.md) — Prompts users to confirm or cancel critical actions (includes co-located BbConfirmPortal export).
1215
- - [BbPopover](./BbPopover.md) — Anchors floating content to reference elements.
1217
+ - [BbPopover](./BbPopover.md) — Anchors floating content to reference elements; also the v-bb-popover directive (its body is a BbPopoverPortal or a component).
@@ -1661,6 +1661,18 @@ listeners attach to the referenced element. ARIA — `aria-haspopup`,
1661
1661
  too, the same as it would to the slot's `props`; don't hand-write your own
1662
1662
  `aria-*` attributes on it, or you get a double-set conflict.)
1663
1663
 
1664
+ **One per row is fine, even thousands.** Until someone presses, focuses or
1665
+ right-clicks the element, the directive builds no component: the element
1666
+ carries `aria-haspopup` / `aria-expanded` itself, and a handful of listeners on
1667
+ `document` serve every sleeping menu. A menu of plain action rows (no
1668
+ selectable group, no fetched `items`) goes back to sleep once it has closed, or
1669
+ when focus leaves an element whose menu never opened, so only the open menu
1670
+ is ever a component. Measured on 3,000 elements: mount and re-render cost
1671
+ about the same as the bare elements. Two things opt a binding out: `eager`, and
1672
+ an explicit `id` (it must stay resolvable by `useBbDropdownContext(id)`). Each
1673
+ then mounts a full `BbDropdown` (about 1 ms per element), so leave both off on
1674
+ long lists.
1675
+
1664
1676
  **Menu on any element, no wrapper**
1665
1677
 
1666
1678
  ```vue
@@ -2249,5 +2261,5 @@ Set these on the element, or on a class you put on it, to retune this component
2249
2261
 
2250
2262
  - [BbDropdownButton](./BbDropdownButton.md) — A button with primary action and all other actions collected in a dropdown.
2251
2263
  - [BbButton](./BbButton.md) — Button with loading state, tooltip, and icon support.
2252
- - [BbPopover](./BbPopover.md) — Anchors floating content to reference elements.
2264
+ - [BbPopover](./BbPopover.md) — Anchors floating content to reference elements; also the v-bb-popover directive (its body is a BbPopoverPortal or a component).
2253
2265
  - [BbBaseButton](./BbBaseButton.md) — Unstyled button/link primitive: element resolution (button/anchor/router link) and the full navigation engine, with no visual chrome. Use for any clickable or navigable surface that is not a variant-first BbButton — cards, list rows, custom links.
@@ -1269,7 +1269,7 @@ import { ref } from 'vue';
1269
1269
  import { BbDropzone, BbButton, BbIcon, BbTextInput } from 'bitboss-ui';
1270
1270
  import type { DropZoneError } from 'bitboss-ui';
1271
1271
 
1272
- const title = ref('');
1272
+ const title = ref<string | null>('');
1273
1273
  const documents = ref<File[]>([]);
1274
1274
  const errors = ref<string[]>([]);
1275
1275
  const submitted = ref<{ field: string; fileCount: number } | null>(null);
@@ -1298,7 +1298,7 @@ const remove = (file: File) => {
1298
1298
  model — assemble a FormData and hand it to your request layer. */
1299
1299
  const submit = () => {
1300
1300
  const body = new FormData();
1301
- body.append('title', title.value);
1301
+ body.append('title', title.value ?? '');
1302
1302
  documents.value.forEach((file) => body.append('documents[]', file));
1303
1303
  submitted.value = { field: 'documents[]', fileCount: documents.value.length };
1304
1304
  };
package/dist/ai/BbForm.md CHANGED
@@ -310,8 +310,8 @@ Four things to know:
310
310
  - **An emptied text field holds `null` by default**, not `''`. So
311
311
  `z.string().min(1, 'Required')` alone answers an emptied field with the
312
312
  library's own "expected string, received null". Two ways out:
313
- - **Set the plugin option `formControls: { textInputNullValue: '',
314
- textareaNullValue: '' }`.** Emptied fields then emit `''` and the short
313
+ - **Set the plugin option `formControls: { textInputNullValue: 'string',
314
+ textareaNullValue: 'string' }`.** Emptied fields then emit `''` and the short
315
315
  schema shows your message as written. Start text values as `''` too (a
316
316
  field nobody touched keeps what your page gave it).
317
317
  - **Or keep `null` and put the message on the type as well:**