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
@@ -0,0 +1,515 @@
1
+ # BbHoverCard
2
+
3
+ ## Agent Contract
4
+
5
+ - Safety level: **Consumer API**
6
+ - Group: **Overlay**
7
+ - Description: A rich preview card that opens on hover or keyboard focus (a person, an event, a link) — BbPopover's panel without the click; may hold commands that also exist elsewhere; also the v-bb-hover-card directive.
8
+ - Usage rule: Prefer this component for consumer-facing implementations.
9
+ - Implementation source (only when the contract below is not enough): [source/BbHoverCard.md](./source/BbHoverCard.md)
10
+
11
+ ## Reachable through `BbHoverCard`
12
+
13
+ `BbHoverCard` mounts other components (all of them: _Component tree_, near the end). These are the parts of its API that land inside them. Prefer these over `:deep()` on an inner class (a part's class is stable within 3.x, any other class is internal and can break on upgrade) — and never an import of an internal component.
14
+
15
+ | pt part | Lands on | In |
16
+ | --- | --- | --- |
17
+ | `pt:panel` | the root of `CommonPopover` | `CommonPopover` _(internal)_ |
18
+ | `pt:root` | the root of `CommonPopover` | `CommonPopover` _(internal)_ |
19
+
20
+ ## Usage & Guidelines
21
+
22
+ ### When to use
23
+
24
+ Reach for `BbHoverCard` to **preview what a link or name points at** without
25
+ leaving the page: a person behind a mention, an event in a calendar, a commit in
26
+ an activity feed. It opens when the pointer rests on the activator (or keyboard
27
+ focus lands on it), stays open while the pointer is over the card, and closes
28
+ when the pointer leaves both.
29
+
30
+ It is `BbPopover`'s panel with a different trigger: same placement, offset,
31
+ width, header / footer bands, `scrollable`, `pt` parts and look. What differs
32
+ is that nothing clicks it open, it never takes focus, and it is not a dialog.
33
+
34
+ Pick a sibling when the shape is different:
35
+
36
+ - One line of text that names or explains the control → `BbTooltip`.
37
+ - Content the user works in (a form, a picker, settings) → `BbPopover`, which
38
+ opens on click and holds focus.
39
+ - A list of actions → `BbDropdown`.
40
+
41
+ **The rule that makes a hover card safe: everything in it must also be
42
+ reachable another way.** A touch screen never shows it (there is no hover), and
43
+ a screen-reader user in browse mode may never open it. The card is a shortcut
44
+ and a preview, never the only path to a command or a fact.
45
+
46
+ ### Activator and content
47
+
48
+ Render the activator in the `activator` slot and **spread its `props`** onto it.
49
+ `props` carries the `id`, the positioning `ref`, the pointer / focus / keyboard
50
+ listeners, and — once the card holds a control — `aria-expanded` /
51
+ `aria-controls`. The activator must be focusable, so a keyboard user can open
52
+ the card too: a link (`<a href>`) is the usual shape, since a hover card
53
+ previews where the link goes. The `default`, `header` and `footer` slots get
54
+ `close`, `open`, `placement` and `disabled`, exactly as `BbPopover`'s do.
55
+
56
+ **A person behind a name**
57
+
58
+ ```vue
59
+ <template>
60
+ <p class="max-w-md text-sm text-(--bb-text-muted)">
61
+ Deploy of
62
+ <span class="font-medium text-(--bb-text)">invoice-export</span>
63
+ approved by
64
+ <BbHoverCard placement="bottom-start" :width="280">
65
+ <template #activator="{ props }">
66
+ <a
67
+ v-bind="props"
68
+ class="font-medium text-(--bb-text) underline decoration-dotted underline-offset-2"
69
+ href="#profile-maria-bianchi"
70
+ >
71
+ {{ member.name }}
72
+ </a>
73
+ </template>
74
+ <div class="flex items-center gap-2.5">
75
+ <BbAvatar :alt="member.name" size="48" :src="member.src">
76
+ <span>{{ member.initials }}</span>
77
+ </BbAvatar>
78
+ <div class="min-w-0">
79
+ <div class="truncate text-sm font-medium text-(--bb-text)">
80
+ {{ member.name }}
81
+ </div>
82
+ <div class="truncate text-xs text-(--bb-text-muted)">
83
+ {{ member.role }}
84
+ </div>
85
+ </div>
86
+ </div>
87
+ <div class="mt-3 grid gap-1 text-xs text-(--bb-text-muted)">
88
+ <div class="flex items-center gap-1.5">
89
+ <BbIcon icon="lucide:map-pin" size="xs" />
90
+ <span>{{ member.location }}</span>
91
+ </div>
92
+ <div class="flex items-center gap-1.5">
93
+ <BbIcon icon="lucide:clock" size="xs" />
94
+ <span>{{ member.localTime }}</span>
95
+ </div>
96
+ </div>
97
+ </BbHoverCard>
98
+ 2 hours ago.
99
+ </p>
100
+ </template>
101
+
102
+ <script setup lang="ts">
103
+ import { BbAvatar, BbHoverCard, BbIcon } from 'bitboss-ui';
104
+ // Each portrait is an inline SVG data URI, so the example loads nothing over
105
+ // the network. Any image URL works here; swap in your own.
106
+ const face = (bg: string, fg: string) =>
107
+ `data:image/svg+xml,${encodeURIComponent(
108
+ `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><rect width="64" height="64" fill="${bg}"/><circle cx="32" cy="25" r="11" fill="${fg}"/><path d="M11 64c1-13 10-20 21-20s20 7 21 20z" fill="${fg}"/></svg>`
109
+ )}`;
110
+
111
+ const member = {
112
+ name: 'Maria Bianchi',
113
+ initials: 'MB',
114
+ role: 'Platform engineer · Infra',
115
+ location: 'Milan, Italy',
116
+ localTime: '16:42 local time',
117
+ src: face('#ffedd5', '#a24a12'),
118
+ };
119
+ </script>
120
+ ```
121
+
122
+ ### Commands in the card
123
+
124
+ A card may hold shortcuts — "Open", "Reschedule", "Follow" — as long as each
125
+ also exists elsewhere (the event page, the profile). With a control inside, the
126
+ card becomes a group named after its activator, and the keyboard can reach it:
127
+ **Tab** from the activator moves into the card, Tab past its last control moves
128
+ on to whatever follows the activator (and closes the card), **Shift+Tab** from
129
+ its first control goes back to the activator, and **Escape** closes it and puts
130
+ focus back on the activator. Prefer `href` links for navigation; `@click` works
131
+ too, and the slot's `close` dismisses the card from inside.
132
+
133
+ **Calendar events with shortcuts**
134
+
135
+ ```vue
136
+ <template>
137
+ <div class="grid max-w-xs gap-1.5">
138
+ <BbHoverCard
139
+ v-for="event in events"
140
+ :key="event.id"
141
+ :offset="8"
142
+ placement="right-start"
143
+ :width="260"
144
+ >
145
+ <template #activator="{ props }">
146
+ <a
147
+ v-bind="props"
148
+ class="block rounded-(--bb-radius) border-l-4 border-(--bb-primary) bg-(--bb-muted) px-2 py-1 text-sm text-(--bb-text)"
149
+ :href="`#event-${event.id}`"
150
+ >
151
+ {{ event.time }} · {{ event.title }}
152
+ </a>
153
+ </template>
154
+ <template #header>
155
+ <div class="text-sm font-medium">{{ event.title }}</div>
156
+ <div class="text-xs text-(--bb-text-muted)">
157
+ {{ event.day }}, {{ event.time }}–{{ event.end }}
158
+ </div>
159
+ </template>
160
+ <div class="grid gap-1 text-xs text-(--bb-text-muted)">
161
+ <div class="flex items-center gap-1.5">
162
+ <BbIcon icon="lucide:map-pin" size="xs" />
163
+ <span>{{ event.room }}</span>
164
+ </div>
165
+ <div class="flex items-center gap-1.5">
166
+ <BbIcon icon="lucide:users" size="xs" />
167
+ <span>{{ event.people }}</span>
168
+ </div>
169
+ </div>
170
+ <template #footer>
171
+ <!-- Shortcuts only: the event page offers both actions too. -->
172
+ <div class="flex gap-2">
173
+ <BbButton :href="`#event-${event.id}`" size="sm" variant="outline">
174
+ Open
175
+ </BbButton>
176
+ <BbButton
177
+ :href="`#event-${event.id}-reschedule`"
178
+ size="sm"
179
+ variant="ghost"
180
+ >
181
+ Reschedule
182
+ </BbButton>
183
+ </div>
184
+ </template>
185
+ </BbHoverCard>
186
+ </div>
187
+ </template>
188
+
189
+ <script setup lang="ts">
190
+ import { BbButton, BbHoverCard, BbIcon } from 'bitboss-ui';
191
+
192
+ const events = [
193
+ {
194
+ id: 'standup',
195
+ title: 'Team stand-up',
196
+ day: 'Tue 7 Oct',
197
+ time: '09:30',
198
+ end: '09:45',
199
+ room: 'Room Brera',
200
+ people: '6 people',
201
+ },
202
+ {
203
+ id: 'review',
204
+ title: 'Design review',
205
+ day: 'Tue 7 Oct',
206
+ time: '11:00',
207
+ end: '12:00',
208
+ room: 'Video call',
209
+ people: 'Ana, Luca, Maria',
210
+ },
211
+ ];
212
+ </script>
213
+ ```
214
+
215
+ ### Timing and the safe zone
216
+
217
+ `delay` (default `350` ms, the same prop name as `BbTooltip`'s) is how long the
218
+ pointer has to rest on the activator before the card opens — long enough that a
219
+ pointer passing over a list of names doesn't flash a card for each. Keyboard
220
+ focus opens it after `delay` too. Closing has no prop: once the pointer has left
221
+ both the activator and the card, the card waits a fixed 200 ms, the time the
222
+ pointer needs to cross the gap into the card.
223
+
224
+ Between the activator and the card there is a **safe zone**: the triangle from
225
+ the point where the pointer left toward the card's corners. While the pointer
226
+ moves inside it, the card stays open, so a diagonal path to a button in the
227
+ card never closes it on the way. A pointer that stops there, or leaves it, lets
228
+ those 200 ms run out.
229
+
230
+ A click on the activator never toggles the card: the link does what links do.
231
+
232
+ ### Interaction: it gives way to what the user opened
233
+
234
+ A hover card is something the pointer opened on its way past, so it gets out
235
+ of the way of anything the user opened on purpose, with no prop to set:
236
+
237
+ - **A press on the trigger closes it at once.** A click, or a right-click
238
+ that opens a context menu on the same row, closes the card with no close
239
+ delay.
240
+ - **While a menu, popover, dialog, select or picker is open, cards on
241
+ triggers outside it don't open**, and an open one closes. A trigger inside
242
+ that overlay (a name in a dialog, a mention in a popover) still works.
243
+ - **Nothing reopens by itself.** When the overlay closes, the next hover or
244
+ focus opens the card as usual.
245
+
246
+ So a table row can carry both `v-bb-dropdown` (a right-click menu) and
247
+ `v-bb-hover-card`: right-click and the card goes and the menu opens; move back
248
+ over the row and no card covers the menu; close it and the next hover shows
249
+ the card. The same holds for `BbTooltip` / `v-bb-tooltip`.
250
+
251
+ ### Placement, offset and width
252
+
253
+ The same props as `BbPopover`, with the same defaults: `placement` (`bottom`,
254
+ auto-flipping near an edge), `offset` (`4`), `padding` (`10`), `boundary`, and
255
+ `width` in pixels or as a percentage of the activator. A hover card never has an
256
+ arrow. `placement="cursor"` is `BbPopover`'s alone (it anchors at a click
257
+ point).
258
+
259
+ **Placement around the activator**
260
+
261
+ ```vue
262
+ <div class="flex flex-wrap gap-4 text-sm">
263
+ <BbHoverCard
264
+ v-for="side in ['top', 'right', 'bottom', 'left']"
265
+ :key="side"
266
+ :placement="side"
267
+ :width="160"
268
+ >
269
+ <template #activator="{ props }">
270
+ <a v-bind="props" :href="`#${side}`" class="underline">{{ side }}</a>
271
+ </template>
272
+ Placed <strong>{{ side }}</strong>
273
+ </BbHoverCard>
274
+ </div>
275
+ ```
276
+
277
+ ### One per row: `v-bb-hover-card`
278
+
279
+ For a card on every row of a table, every event of a calendar or every mention
280
+ in a feed, the directive keeps the page light: no component exists until the
281
+ pointer or keyboard focus reaches the element, and it goes away again once the
282
+ card has closed. A thousand rows with `v-bb-hover-card` cost what a thousand
283
+ plain links cost; a thousand `<BbHoverCard>` components cost two thousand
284
+ component instances before anyone hovers.
285
+
286
+ A directive has no slot, so the card's body is a **portal**, as in
287
+ `BbConfirm`:
288
+
289
+ - **A named portal:** `v-bb-hover-card="'person-card'"` opens the
290
+ `<BbHoverCardPortal name="person-card">` declared once in the template. Mind
291
+ the inner quotes: the attribute value is an expression, so without them
292
+ `person-card` is `person` minus `card`, and nothing opens (`check` flags it).
293
+ The portal's slot gets `{ props, close }`.
294
+ - **A component:** `v-bb-hover-card="PersonCard"` renders `PersonCard` in the
295
+ card. It can close the card with `emit('close')`.
296
+ - **The object form** takes either one as `portal`, data for it as
297
+ `portalProps` (the named portal's `props`, or the component's real props),
298
+ and any `BbHoverCard` prop: `{ portal: 'person-card', portalProps: { person },
299
+ width: 220 }`. The argument is the placement: `v-bb-hover-card:right`.
300
+
301
+ The portal's content sees the page around it, as if it were written inline:
302
+ a component portal injects from where the trigger is, a named portal from
303
+ where its `<BbHoverCardPortal>` is declared.
304
+
305
+ **A card per row, from the directive**
306
+
307
+ ```vue
308
+ <template>
309
+ <ul class="grid max-w-xs gap-1 text-sm">
310
+ <li v-for="person in people" :key="person.id">
311
+ <!-- The inner quotes make it a name: "'person-card'". -->
312
+ <a
313
+ v-bb-hover-card:right="{
314
+ portal: 'person-card',
315
+ portalProps: { person },
316
+ width: 220,
317
+ }"
318
+ class="text-(--bb-text) underline decoration-dotted underline-offset-2"
319
+ :href="`#person-${person.id}`"
320
+ >
321
+ {{ person.name }}
322
+ </a>
323
+ </li>
324
+ </ul>
325
+
326
+ <!-- Declared once; renders only inside the card that opens. -->
327
+ <!-- Portal props are untyped (like BbConfirm's): name the shape with `as`. -->
328
+ <BbHoverCardPortal v-slot="{ props }" name="person-card">
329
+ <div class="grid gap-0.5">
330
+ <div class="font-medium text-(--bb-text)">
331
+ {{ (props.person as Person).name }}
332
+ </div>
333
+ <div class="text-xs text-(--bb-text-muted)">
334
+ {{ (props.person as Person).team }} ·
335
+ {{ (props.person as Person).city }}
336
+ </div>
337
+ </div>
338
+ </BbHoverCardPortal>
339
+ </template>
340
+
341
+ <script setup lang="ts">
342
+ import { BbHoverCardPortal, vBbHoverCard } from 'bitboss-ui';
343
+
344
+ type Person = { id: number; name: string; team: string; city: string };
345
+
346
+ const people: Person[] = [
347
+ { id: 1, name: 'Ana Ferreira', team: 'Payments', city: 'Lisbon' },
348
+ { id: 2, name: 'Luca Romano', team: 'Search', city: 'Turin' },
349
+ { id: 3, name: 'Maria Bianchi', team: 'Infra', city: 'Milan' },
350
+ ];
351
+ </script>
352
+ ```
353
+
354
+ ### Lifecycle, v-model and lazy content
355
+
356
+ - `show` / `shown` / `hide` / `hidden` fire at the same moments as
357
+ `BbPopover`'s. Focus never moves on open, so `shown` means only that the open
358
+ transition has run.
359
+ - `v-model` reads and drives it: write `true` to open it from elsewhere (a
360
+ keyboard shortcut, a test) and `false` to close it.
361
+ - Content is **lazy**: the body renders on the first open. Until then the
362
+ activator says nothing about the card, because the card doesn't know yet
363
+ whether it will hold a control. `eager` renders it up front.
364
+ - `disabled` keeps it shut, and disabling an open card closes it — set it while
365
+ a drag or a resize runs over the activator.
366
+ - It closes when the activator scrolls out of view, like `BbPopover`.
367
+
368
+ ### Restyling with passthrough
369
+
370
+ The same parts as `BbPopover` minus the phone sheet: `root` (the floating
371
+ element, where a `class` on the component lands), `panel` (the surface you
372
+ see), `header`, `content`, `footer`. State: `open`.
373
+
374
+ **pt:header — a commit preview**
375
+
376
+ ```vue
377
+ <template>
378
+ <BbHoverCard
379
+ placement="bottom-start"
380
+ pt:content="text-xs leading-relaxed"
381
+ pt:header="bg-(--bb-muted) font-mono text-xs"
382
+ pt:panel="overflow-hidden"
383
+ :width="300"
384
+ >
385
+ <template #activator="{ props }">
386
+ <a
387
+ v-bind="props"
388
+ class="font-mono text-sm text-(--bb-text) underline decoration-dotted underline-offset-2"
389
+ href="#commit-4f780d9"
390
+ >
391
+ 4f780d9
392
+ </a>
393
+ </template>
394
+ <template #header>4f780d9 · main</template>
395
+ Hover card allows commands, with the keyboard bridge.
396
+ </BbHoverCard>
397
+ </template>
398
+
399
+ <script setup lang="ts">
400
+ import { BbHoverCard } from 'bitboss-ui';
401
+ </script>
402
+ ```
403
+
404
+ The full grammar is in the [passthrough guide](./guides/passthrough.md). A `pt`
405
+ value written in `<script>` is typed as `BbHoverCardProps['pt']`.
406
+
407
+ ### Works well with
408
+
409
+ - `BbAvatar`, `BbBadge`, `BbIcon` — the usual content of a person or record
410
+ preview.
411
+ - `BbButton` with `href` — a shortcut in the footer band.
412
+ - `BbTable` / `BbCalendar` rows — through `v-bb-hover-card`, one per row.
413
+
414
+ ### Gotchas & anti-patterns
415
+
416
+ - **Don't put the only path to anything in a hover card.** Touch never shows
417
+ it. Every command and every fact in it must also exist on the page it links
418
+ to, or elsewhere on this one.
419
+ - **Don't use it for a form or a picker** — the card closes when the pointer
420
+ wanders off. That is `BbPopover`.
421
+ - **Don't use it for one line of text** — that is `BbTooltip`, which also
422
+ describes the activator to a screen reader.
423
+ - **Don't put it on a non-focusable element** (a bare `<span>`) — keyboard users
424
+ could never open it. Use a link or a button.
425
+ - **Don't open it from `@click`** — a click never toggles a hover card; bind
426
+ `v-model` if something else must open it.
427
+
428
+ ### Accessibility
429
+
430
+ - **Semantics follow the content.** A read-only card has no role and adds
431
+ nothing to the activator: the activator's own name is what a screen reader
432
+ announces, and the card is a visual extra (it is not flattened into
433
+ `aria-describedby` the way `BbTooltip` is). A card that holds a control is a
434
+ `role="group"` named after its activator, and the activator carries
435
+ `aria-expanded` / `aria-controls`. The check runs once the card has rendered
436
+ on open, and again whenever its content changes while open.
437
+ - Only keyboard focus (`:focus-visible`) opens it — not the focus a click
438
+ leaves behind. Focus never moves into the card on its own; Tab does that.
439
+ - Escape closes the innermost open surface first: a hover card inside a dialog
440
+ closes before the dialog.
441
+ - It is not modal: the page behind stays live, and the card is a
442
+ `popover="manual"` element in the top layer, not a `<dialog>`.
443
+
444
+ ## v-model
445
+
446
+ - `v-model` — type: `boolean \| undefined`
447
+
448
+ ## Props
449
+
450
+ | Prop | Type | Default | Required | Description |
451
+ | --- | --- | --- | --- | --- |
452
+ | `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: `content`, `footer`, `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 `BbHoverCardProps['pt']` (`PtValue` is not exported): § Typing `pt` in `<script>`. |
453
+ | `activator` | `ElementOrComponentRef \| undefined` | | | External activator element or component ref. When provided, the activator slot is not rendered and the hover / focus listeners are attached programmatically to the referenced element. |
454
+ | `boundary` | `ElementOrComponentRefOrSelector \| undefined` | | | Constrains the card within a bounding element. When provided, the card will not overflow this container's bounds (e.g. a sidebar). Accepts an HTMLElement, a Vue component ref, or a CSS selector string. |
455
+ | `delay` | `number \| undefined` | `350` | | The delay in milliseconds before the card is shown: how long the pointer rests on the activator, or keyboard focus stays on it. BbTooltip's name; a different default, because a card that opened on every pass of the pointer would be in the way. Closing has a fixed grace instead (see the guide § Timing and the safe zone). |
456
+ | `disabled` | `boolean \| undefined` | `false` | | Disables the component. Disabling an open card closes it. |
457
+ | `eager` | `boolean \| undefined` | `false` | | Prevents rendering content before it's made visible. |
458
+ | `id` | `string \| undefined` | | | The activator's id (the card is `bbhc_<id>`, and is labelled by it once it holds a control). Generated when omitted. |
459
+ | `modelValue` | `boolean \| undefined` | `false` | | Used by v-model to open / close the card. A card with no `v-model` starts closed and is driven by hover and keyboard focus. |
460
+ | `offset` | `number \| undefined` | `4` | | Space the floating container will maintain from the activator. |
461
+ | `padding` | `number \| undefined` | `10` | | Minimum empty space to keep from the edge of the page. |
462
+ | `placement` | `'top'` \| `'bottom'` \| `'left'` \| `'right'` \| `'top-start'` \| `'top-end'` \| `'right-start'` \| `'right-end'` \| `'bottom-start'` \| `'bottom-end'` \| `'left-start'` \| `'left-end'` | `"bottom"` | | Position of the card relative to the activator. |
463
+ | `scrollable` | `boolean \| undefined` | `false` | | Caps the card height to the available viewport space and enables internal scrolling. |
464
+ | `transitionDuration` | `number \| undefined` | `250` | | How long the transition has to last in milliseconds. |
465
+ | `width` | `string \| number \| undefined` | | | Width of the card. Accepts pixels or percentage (relative to activator width). |
466
+
467
+ ## Passthrough parts and states
468
+
469
+ `pt:<part>="…"` restyles a part; `pt:<part>:<state>="…"` applies only while the state is on. A component-wide state is on for every part at once; a _per node_ state resolves on each node of a broadcast part (one row, one option, one day). Grammar and merge rules: `guides/passthrough.md`. In `<script>`, type a `pt` value as `<Component>Props['pt']` (`<Component>Props<Row>['pt']` on a generic component); `PtValue` is not exported: `guides/passthrough.md` § Typing `pt` in `<script>`.
470
+
471
+ | Part | What it is |
472
+ | --- | --- |
473
+ | `root` | The floating element — an invisible positioning wrapper; a `class` on the component lands here, and so does `role="group"` once the card holds a control. |
474
+ | `panel` | The surface you see — the bordered box that paints the card and lays out its bands. |
475
+ | `header` | The header band. |
476
+ | `content` | The body band. |
477
+ | `footer` | The footer band. |
478
+
479
+ States are listed in precedence order: when two are on at once and their entries conflict, the later one wins.
480
+
481
+ | State | When it is on |
482
+ | --- | --- |
483
+ | `open` | Component-wide: the card is open. |
484
+
485
+ ## Events
486
+
487
+ - `hidden` — `(): void` — Emitted after the card is fully closed.
488
+ - `hide` — `(): void` — Emitted as soon as the card starts to close (model becomes false).
489
+ - `show` — `(): void` — Emitted as soon as the card starts to open (model becomes true).
490
+ - `shown` — `(): void` — Emitted after the card is fully opened. Focus never moves on open.
491
+ - `update:modelValue` — `(value: boolean): void` — Emitted with the new value whenever `v-model` changes.
492
+
493
+ ## Slots
494
+
495
+ - `activator` — scope: `BbHoverCardActivatorSlotProps` `{ disabled: boolean; open: boolean; placement: Placement; props: { 'aria-expanded'?: boolean \| undefined; 'aria-controls'?: string \| undefined; id: string; ref: (reference: unknown) => void; onPointerenter: (event: PointerEvent) => void; onPointerleave: (event: PointerEvent) => void; onFocusin: (event: FocusEvent) => void; onFocusout: (event: FocusEvent) => void; onKeydown: (event: KeyboardEvent) => void; onPointerdown: (event: PointerEvent) => void; onContextmenu: (event: MouseEvent) => void; } }` — The element the card describes. Spread `props` onto it; it must be focusable (a link or a button) so a keyboard user can open the card too.
496
+ - `default` — scope: `BbHoverCardDefaultSlotProps` `{ close: () => void; disabled: boolean; open: boolean; placement: Placement }` — The card body.
497
+ - `footer` — scope: `BbHoverCardDefaultSlotProps` `{ close: () => void; disabled: boolean; open: boolean; placement: Placement }` — Content pinned to the bottom of the card, outside the scroll region.
498
+ - `header` — scope: `BbHoverCardDefaultSlotProps` `{ close: () => void; disabled: boolean; open: boolean; placement: Placement }` — Content pinned to the top of the card, outside the scroll region.
499
+
500
+ ## Component tree
501
+
502
+ `BbHoverCard` is not one component: rendering it mounts every component below, and each one is live inside it. **Read this before styling, theming or debugging `BbHoverCard`, and before concluding a part, a token or a behaviour does not exist** — it may live on a component further down. How to read it:
503
+
504
+ - **_internal_** nodes are building blocks with no API of their own: you reach them only through `BbHoverCard`. A pt part listed as landing there is set on `BbHoverCard` (`pt:<part>`).
505
+ - **_public_** nodes are components with their own contract. `BbHoverCard` reaches one only through a pt part of its own that lands on it (shown on the parent node as `part → Component`, e.g. `spinner → BbSpinner`) or through a pt map it hands it (shown as "hands … the pt map", in `BbHoverCard`'s words → the child's). The child's other props and parts are not exposed; its contract explains what it renders.
506
+ - **CSS custom properties** listed on a node are declared on THAT node's root and read by elements inside it. Set one on that node's root or on the element inside it that reads it — the pt part that lands there is how you reach it (`pt:box="[--border-color:…]"` on a text input) — never on `BbHoverCard`'s root, where the node's own declaration masks it (design-tokens guide, _Overriding one from a consumer app_, rule 0).
507
+ - Listed nodes are the ones `BbHoverCard`'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/BbHoverCard.tree.json`.
508
+
509
+ - `BbHoverCard` _(this component)_ — its own template binds: `panel` → `CommonPopover`, `root` → `CommonPopover`, `header` → `span.bb-hover-card__header`, `content` → `span.bb-hover-card__content`, `footer` → `span.bb-hover-card__footer`
510
+ - Also mounts `CommonPopover`
511
+
512
+ ## See Also
513
+
514
+ - [BbPopover](./BbPopover.md) — Anchors floating content to reference elements; also the v-bb-popover directive (its body is a BbPopoverPortal or a component).
515
+ - [BbTooltip](./BbTooltip.md) — Displays contextual helper text on interaction.
@@ -0,0 +1,29 @@
1
+ # BbHoverCardPortal
2
+
3
+ ## Agent Contract
4
+
5
+ - Safety level: **Consumer API**
6
+ - Group: **Overlay**
7
+ - Description: Registers named slot content that `v-bb-hover-card="'name'"` (or `{ portal: 'name' }`) renders in the card; slot `{ props, close }`.
8
+ - Usage rule: Prefer this component for consumer-facing implementations.
9
+ - Implementation source (only when the contract below is not enough): [source/BbHoverCardPortal.md](./source/BbHoverCardPortal.md)
10
+
11
+ ## Props
12
+
13
+ | Prop | Type | Default | Required | Description |
14
+ | --- | --- | --- | --- | --- |
15
+ | `name` | `string` | | yes | The name a `v-bb-hover-card` passes as its value (`"'name'"`) or as `portal: 'name'`. |
16
+
17
+ ## Events
18
+
19
+ - None.
20
+
21
+ ## Slots
22
+
23
+ - `default` — scope: `DirectivePortalSlotProps` `{ props: Record<string, unknown>; close: () => void }` — The card's body. `props` is the directive's `portalProps`; `close` closes the card.
24
+
25
+ ## See Also
26
+
27
+ - [BbHoverCard](./BbHoverCard.md) — A rich preview card that opens on hover or keyboard focus (a person, an event, a link) — BbPopover's panel without the click; may hold commands that also exist elsewhere; also the v-bb-hover-card directive.
28
+ - [BbPopoverPortal](./BbPopoverPortal.md) — Registers named slot content that `v-bb-popover="'name'"` (or `{ portal: 'name' }`) renders in the popover; slot `{ props, close }`.
29
+ - [BbConfirmPortal](./BbConfirmPortal.md) — Registers named slot content that confirm() renders via `portal`.
@@ -216,7 +216,7 @@ header and footer stay pinned.
216
216
  <div class="flex justify-end gap-2">
217
217
  <BbButton variant="ghost" @click="open = false">Cancel</BbButton>
218
218
  <BbButton
219
- :disabled="!email.trim()"
219
+ :disabled="!email?.trim()"
220
220
  variant="primary"
221
221
  @click="open = false"
222
222
  >
@@ -232,7 +232,7 @@ import { ref } from 'vue';
232
232
  import { BbButton, BbOffCanvas, BbTextInput } from 'bitboss-ui';
233
233
 
234
234
  const open = ref(false);
235
- const email = ref('');
235
+ const email = ref<string | null>('');
236
236
  </script>
237
237
  ```
238
238
 
@@ -951,7 +951,7 @@ import { ref } from 'vue';
951
951
  import { BbButton, BbOffCanvas, BbTextInput } from 'bitboss-ui';
952
952
 
953
953
  const open = ref(false);
954
- const reviewer = ref('');
954
+ const reviewer = ref<string | null>('');
955
955
  </script>
956
956
  ```
957
957
 
@@ -1268,4 +1268,4 @@ Set these on the element, or on a class you put on it, to retune this component
1268
1268
  ## See Also
1269
1269
 
1270
1270
  - [BbDialog](./BbDialog.md) — Presents modal dialogs for focused user interactions.
1271
- - [BbPopover](./BbPopover.md) — Anchors floating content to reference elements.
1271
+ - [BbPopover](./BbPopover.md) — Anchors floating content to reference elements; also the v-bb-popover directive (its body is a BbPopoverPortal or a component).