@bitforgehq/ui 0.3.0

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 (281) hide show
  1. package/CHANGELOG.md +3665 -0
  2. package/LICENSE +21 -0
  3. package/README.md +175 -0
  4. package/ai/MANIFEST.index.json +383 -0
  5. package/ai/MANIFEST.json +7528 -0
  6. package/ai/REFERENCE.index.json +1461 -0
  7. package/ai/REFERENCE.json +7213 -0
  8. package/ai/UI_GUIDE.md +1159 -0
  9. package/ai/components/action-bar.md +209 -0
  10. package/ai/components/action-sheet.md +526 -0
  11. package/ai/components/alert-dialog.md +509 -0
  12. package/ai/components/alert.md +376 -0
  13. package/ai/components/avatar.md +146 -0
  14. package/ai/components/back-button.md +286 -0
  15. package/ai/components/badge.md +171 -0
  16. package/ai/components/button.md +431 -0
  17. package/ai/components/card.md +303 -0
  18. package/ai/components/checkbox.md +418 -0
  19. package/ai/components/chip-group.md +205 -0
  20. package/ai/components/chip.md +300 -0
  21. package/ai/components/content.md +474 -0
  22. package/ai/components/desktop-toast.md +420 -0
  23. package/ai/components/dialog.md +962 -0
  24. package/ai/components/empty-state.md +146 -0
  25. package/ai/components/field.md +319 -0
  26. package/ai/components/header.md +809 -0
  27. package/ai/components/icon-button.md +221 -0
  28. package/ai/components/icon.md +176 -0
  29. package/ai/components/input.md +455 -0
  30. package/ai/components/list-item.md +326 -0
  31. package/ai/components/list.md +178 -0
  32. package/ai/components/menu-button.md +377 -0
  33. package/ai/components/menu.md +661 -0
  34. package/ai/components/modal.md +613 -0
  35. package/ai/components/number-input.md +423 -0
  36. package/ai/components/page.md +299 -0
  37. package/ai/components/progress.md +215 -0
  38. package/ai/components/radio-group.md +432 -0
  39. package/ai/components/refresher.md +377 -0
  40. package/ai/components/segmented.md +517 -0
  41. package/ai/components/select.md +510 -0
  42. package/ai/components/sheet.md +845 -0
  43. package/ai/components/spinner.md +171 -0
  44. package/ai/components/stack-outlet.md +661 -0
  45. package/ai/components/tab-bar.md +427 -0
  46. package/ai/components/tab.md +295 -0
  47. package/ai/components/tabs.md +408 -0
  48. package/ai/components/textarea.md +371 -0
  49. package/ai/components/toast.md +522 -0
  50. package/ai/components/toggle.md +387 -0
  51. package/ai/reference/bf-action-directive.md +21 -0
  52. package/ai/reference/bf-action-sheet-action-role.md +27 -0
  53. package/ai/reference/bf-action-sheet-action.md +28 -0
  54. package/ai/reference/bf-action-sheet-options.md +28 -0
  55. package/ai/reference/bf-action-sheet-service.md +33 -0
  56. package/ai/reference/bf-alert-dialog-button-role.md +27 -0
  57. package/ai/reference/bf-alert-dialog-button.md +27 -0
  58. package/ai/reference/bf-alert-dialog-options.md +29 -0
  59. package/ai/reference/bf-alert-dialog-service.md +34 -0
  60. package/ai/reference/bf-alert-variant.md +28 -0
  61. package/ai/reference/bf-autocapitalize.md +30 -0
  62. package/ai/reference/bf-avatar-size.md +27 -0
  63. package/ai/reference/bf-avatar-variant.md +28 -0
  64. package/ai/reference/bf-back-at-root.md +28 -0
  65. package/ai/reference/bf-back-button-service.md +40 -0
  66. package/ai/reference/bf-badge-variant.md +29 -0
  67. package/ai/reference/bf-button-size.md +29 -0
  68. package/ai/reference/bf-button-type.md +28 -0
  69. package/ai/reference/bf-button-variant.md +29 -0
  70. package/ai/reference/bf-card-padding.md +26 -0
  71. package/ai/reference/bf-card-variant.md +28 -0
  72. package/ai/reference/bf-chip-group-mode.md +27 -0
  73. package/ai/reference/bf-chip-variant.md +26 -0
  74. package/ai/reference/bf-compare-with.md +24 -0
  75. package/ai/reference/bf-component-overlay-ref.md +33 -0
  76. package/ai/reference/bf-confirm-button-role.md +26 -0
  77. package/ai/reference/bf-confirm-button.md +27 -0
  78. package/ai/reference/bf-confirm-options.md +31 -0
  79. package/ai/reference/bf-confirm-size.md +26 -0
  80. package/ai/reference/bf-content-scroll-event.md +21 -0
  81. package/ai/reference/bf-defaults-options.md +32 -0
  82. package/ai/reference/bf-defaults.md +28 -0
  83. package/ai/reference/bf-desktop-toast-config.md +28 -0
  84. package/ai/reference/bf-desktop-toast-harness.md +33 -0
  85. package/ai/reference/bf-desktop-toast-options.md +28 -0
  86. package/ai/reference/bf-desktop-toast-position.md +28 -0
  87. package/ai/reference/bf-desktop-toast-service.md +42 -0
  88. package/ai/reference/bf-dialog-config.md +44 -0
  89. package/ai/reference/bf-dialog-harness.md +52 -0
  90. package/ai/reference/bf-dialog-ref.md +44 -0
  91. package/ai/reference/bf-dialog-role.md +26 -0
  92. package/ai/reference/bf-dialog-service.md +55 -0
  93. package/ai/reference/bf-dialog-size.md +31 -0
  94. package/ai/reference/bf-dialog-template-context.md +32 -0
  95. package/ai/reference/bf-empty-state-heading-level.md +26 -0
  96. package/ai/reference/bf-enter-key-hint.md +31 -0
  97. package/ai/reference/bf-field-control-class.md +32 -0
  98. package/ai/reference/bf-field-control-state.md +28 -0
  99. package/ai/reference/bf-field-size.md +29 -0
  100. package/ai/reference/bf-haptics-impact-style.md +27 -0
  101. package/ai/reference/bf-haptics-notification-type.md +27 -0
  102. package/ai/reference/bf-haptics-service.md +41 -0
  103. package/ai/reference/bf-harness-filters.md +29 -0
  104. package/ai/reference/bf-horizontal-scroll-directive.md +129 -0
  105. package/ai/reference/bf-icon-button-variant.md +28 -0
  106. package/ai/reference/bf-icon-size.md +27 -0
  107. package/ai/reference/bf-input-autocapitalize.md +36 -0
  108. package/ai/reference/bf-input-enter-key-hint.md +38 -0
  109. package/ai/reference/bf-input-input-mode.md +38 -0
  110. package/ai/reference/bf-input-mode.md +32 -0
  111. package/ai/reference/bf-input-type.md +35 -0
  112. package/ai/reference/bf-jsdom-shim-options.md +22 -0
  113. package/ai/reference/bf-keyboard-assist-behavior.md +26 -0
  114. package/ai/reference/bf-keyboard-assist-directive.md +185 -0
  115. package/ai/reference/bf-keyboard-phase.md +30 -0
  116. package/ai/reference/bf-keyboard-service.md +50 -0
  117. package/ai/reference/bf-labels-overrides.md +26 -0
  118. package/ai/reference/bf-labels.md +44 -0
  119. package/ai/reference/bf-menu-button-harness.md +40 -0
  120. package/ai/reference/bf-menu-button-variant.md +27 -0
  121. package/ai/reference/bf-menu-divider-directive.md +13 -0
  122. package/ai/reference/bf-menu-group-directive.md +14 -0
  123. package/ai/reference/bf-menu-harness.md +55 -0
  124. package/ai/reference/bf-menu-item-checkbox-directive.md +31 -0
  125. package/ai/reference/bf-menu-item-directive.md +31 -0
  126. package/ai/reference/bf-menu-item-harness.md +52 -0
  127. package/ai/reference/bf-menu-item-radio-directive.md +29 -0
  128. package/ai/reference/bf-menu-placement.md +29 -0
  129. package/ai/reference/bf-menu-trigger-directive.md +46 -0
  130. package/ai/reference/bf-modal-options.md +30 -0
  131. package/ai/reference/bf-modal-presentation.md +26 -0
  132. package/ai/reference/bf-modal-service.md +38 -0
  133. package/ai/reference/bf-nav-controller.md +59 -0
  134. package/ai/reference/bf-nav-direction.md +23 -0
  135. package/ai/reference/bf-nav-target.md +21 -0
  136. package/ai/reference/bf-number-input-mode.md +28 -0
  137. package/ai/reference/bf-number-input-step.md +25 -0
  138. package/ai/reference/bf-overlay-auto-focus.md +29 -0
  139. package/ai/reference/bf-overlay-controller.md +22 -0
  140. package/ai/reference/bf-overlay-host.md +36 -0
  141. package/ai/reference/bf-overlay-options.md +53 -0
  142. package/ai/reference/bf-overlay-position.md +30 -0
  143. package/ai/reference/bf-overlay-ref.md +53 -0
  144. package/ai/reference/bf-overlay-role.md +27 -0
  145. package/ai/reference/bf-overlay-service.md +53 -0
  146. package/ai/reference/bf-overlay-stack-options.md +24 -0
  147. package/ai/reference/bf-overlay-stack.md +81 -0
  148. package/ai/reference/bf-overlay-template-context.md +24 -0
  149. package/ai/reference/bf-overscroll-stretch-directive.md +163 -0
  150. package/ai/reference/bf-page-context.md +24 -0
  151. package/ai/reference/bf-platform-service.md +53 -0
  152. package/ai/reference/bf-platform.md +28 -0
  153. package/ai/reference/bf-progress-size.md +26 -0
  154. package/ai/reference/bf-progress-tone.md +28 -0
  155. package/ai/reference/bf-radio-group-harness.md +49 -0
  156. package/ai/reference/bf-radio-group-orientation.md +26 -0
  157. package/ai/reference/bf-radio-option.md +30 -0
  158. package/ai/reference/bf-refresher-refresh-event.md +22 -0
  159. package/ai/reference/bf-segment-option.md +29 -0
  160. package/ai/reference/bf-segmented-harness.md +48 -0
  161. package/ai/reference/bf-segmented-size.md +29 -0
  162. package/ai/reference/bf-select-harness.md +70 -0
  163. package/ai/reference/bf-select-option.md +29 -0
  164. package/ai/reference/bf-sheet-auto-focus.md +29 -0
  165. package/ai/reference/bf-sheet-options.md +32 -0
  166. package/ai/reference/bf-sheet-ref.md +45 -0
  167. package/ai/reference/bf-sheet-service.md +34 -0
  168. package/ai/reference/bf-shell-config-mode.md +28 -0
  169. package/ai/reference/bf-shell-config.md +38 -0
  170. package/ai/reference/bf-shell-dynamic-type-limit.md +21 -0
  171. package/ai/reference/bf-shell-dynamic-type.md +25 -0
  172. package/ai/reference/bf-shell-mode.md +28 -0
  173. package/ai/reference/bf-shell-testing-options.md +26 -0
  174. package/ai/reference/bf-size.md +45 -0
  175. package/ai/reference/bf-spinner-size.md +27 -0
  176. package/ai/reference/bf-stack-change.md +22 -0
  177. package/ai/reference/bf-stack-reuse-strategy.md +32 -0
  178. package/ai/reference/bf-stack-transition-event.md +26 -0
  179. package/ai/reference/bf-stack-transition.md +26 -0
  180. package/ai/reference/bf-stacked-overlay.md +23 -0
  181. package/ai/reference/bf-system-bars-style.md +38 -0
  182. package/ai/reference/bf-tab-bar-controller.md +51 -0
  183. package/ai/reference/bf-tab-bar-host.md +52 -0
  184. package/ai/reference/bf-tab-context.md +32 -0
  185. package/ai/reference/bf-tab-links.md +37 -0
  186. package/ai/reference/bf-testing-options.md +27 -0
  187. package/ai/reference/bf-text-scale-service.md +35 -0
  188. package/ai/reference/bf-theme.md +27 -0
  189. package/ai/reference/bf-toast-action.md +25 -0
  190. package/ai/reference/bf-toast-controller.md +17 -0
  191. package/ai/reference/bf-toast-dismiss-reason.md +29 -0
  192. package/ai/reference/bf-toast-options.md +29 -0
  193. package/ai/reference/bf-toast-position.md +26 -0
  194. package/ai/reference/bf-toast-ref.md +41 -0
  195. package/ai/reference/bf-toast-service.md +39 -0
  196. package/ai/reference/bf-toggle-label-position.md +26 -0
  197. package/ai/reference/bf-transition-kind.md +28 -0
  198. package/ai/reference/bf-validation-message.md +31 -0
  199. package/ai/reference/bf-validation-messages.md +33 -0
  200. package/ai/reference/bf-view-event.md +28 -0
  201. package/ai/reference/bf-view-lifecycle.md +41 -0
  202. package/ai/reference/bf-view-state.md +28 -0
  203. package/ai/reference/bf_default_labels.md +21 -0
  204. package/ai/reference/bf_default_values.md +21 -0
  205. package/ai/reference/bf_defaults.md +21 -0
  206. package/ai/reference/bf_desktop_toast_defaults.md +21 -0
  207. package/ai/reference/bf_dialog_data.md +17 -0
  208. package/ai/reference/bf_labels.md +21 -0
  209. package/ai/reference/bf_overlay_data.md +19 -0
  210. package/ai/reference/bf_overlay_host.md +21 -0
  211. package/ai/reference/bf_sheet_default_snap_points.md +17 -0
  212. package/ai/reference/bf_shell_config.md +21 -0
  213. package/ai/reference/bf_shell_defaults.md +21 -0
  214. package/ai/reference/bf_tab_bar.md +21 -0
  215. package/ai/reference/bf_tab_bar_controller.md +21 -0
  216. package/ai/reference/bf_tab_context.md +21 -0
  217. package/ai/reference/bf_tab_links.md +21 -0
  218. package/ai/reference/bf_theme_storage.md +19 -0
  219. package/ai/reference/create-transition.md +36 -0
  220. package/ai/reference/duration_fast.md +17 -0
  221. package/ai/reference/duration_md.md +17 -0
  222. package/ai/reference/duration_overlay.md +17 -0
  223. package/ai/reference/duration_push.md +17 -0
  224. package/ai/reference/fade-down.md +26 -0
  225. package/ai/reference/fade-in.md +25 -0
  226. package/ai/reference/fade-out.md +25 -0
  227. package/ai/reference/fade-up.md +26 -0
  228. package/ai/reference/inject-shell-mode.md +23 -0
  229. package/ai/reference/install-bf-jsdom-shims.md +31 -0
  230. package/ai/reference/ios_duration.md +17 -0
  231. package/ai/reference/ios_easing.md +17 -0
  232. package/ai/reference/is-text-entry.md +25 -0
  233. package/ai/reference/max_complete_ms.md +17 -0
  234. package/ai/reference/md-transition.md +31 -0
  235. package/ai/reference/md_duration.md +17 -0
  236. package/ai/reference/md_easing.md +17 -0
  237. package/ai/reference/overlay_duration.md +21 -0
  238. package/ai/reference/overlay_easing.md +19 -0
  239. package/ai/reference/overlay_reduced_duration.md +17 -0
  240. package/ai/reference/pop-frames-at.md +25 -0
  241. package/ai/reference/prefers-reduced-motion.md +25 -0
  242. package/ai/reference/provide-bf-defaults.md +33 -0
  243. package/ai/reference/provide-bf-desktop-toast.md +28 -0
  244. package/ai/reference/provide-bf-labels.md +38 -0
  245. package/ai/reference/provide-bf-shell-testing.md +34 -0
  246. package/ai/reference/provide-bf-shell.md +41 -0
  247. package/ai/reference/provide-bf-testing.md +33 -0
  248. package/ai/reference/reduced-transition.md +28 -0
  249. package/ai/reference/reduced_duration.md +17 -0
  250. package/ai/reference/resolve-shell-mode.md +33 -0
  251. package/ai/reference/scale-in.md +25 -0
  252. package/ai/reference/settle-duration.md +25 -0
  253. package/ai/reference/settle-pop.md +33 -0
  254. package/ai/reference/settle_easing.md +17 -0
  255. package/ai/reference/should-complete-pop.md +26 -0
  256. package/ai/reference/slide-down.md +26 -0
  257. package/ai/reference/slide-up.md +26 -0
  258. package/ai/reference/with-bf-shell-routing.md +19 -0
  259. package/bin/bf-ui.js +81 -0
  260. package/fesm2022/bitforgehq-ui-icons.mjs +230 -0
  261. package/fesm2022/bitforgehq-ui-icons.mjs.map +1 -0
  262. package/fesm2022/bitforgehq-ui-mobile.mjs +5530 -0
  263. package/fesm2022/bitforgehq-ui-mobile.mjs.map +1 -0
  264. package/fesm2022/bitforgehq-ui-number-input.mjs +680 -0
  265. package/fesm2022/bitforgehq-ui-number-input.mjs.map +1 -0
  266. package/fesm2022/bitforgehq-ui-overlay.mjs +2675 -0
  267. package/fesm2022/bitforgehq-ui-overlay.mjs.map +1 -0
  268. package/fesm2022/bitforgehq-ui-testing.mjs +402 -0
  269. package/fesm2022/bitforgehq-ui-testing.mjs.map +1 -0
  270. package/fesm2022/bitforgehq-ui.mjs +7765 -0
  271. package/fesm2022/bitforgehq-ui.mjs.map +1 -0
  272. package/llms-full.txt +25394 -0
  273. package/llms.txt +329 -0
  274. package/package.json +94 -0
  275. package/theme.css +253 -0
  276. package/types/bitforgehq-ui-icons.d.ts +50 -0
  277. package/types/bitforgehq-ui-mobile.d.ts +2914 -0
  278. package/types/bitforgehq-ui-number-input.d.ts +282 -0
  279. package/types/bitforgehq-ui-overlay.d.ts +1514 -0
  280. package/types/bitforgehq-ui-testing.d.ts +495 -0
  281. package/types/bitforgehq-ui.d.ts +3499 -0
@@ -0,0 +1,661 @@
1
+ # bf-menu
2
+
3
+ Keyboard-operable menu panel with items, checkbox and radio items, groups and submenus
4
+
5
+ `BfMenuComponent` from `@bitforgehq/ui/overlay` · Overlays · since 0.3.0
6
+
7
+ ## Example
8
+
9
+ ```ts
10
+ import { Component, signal } from '@angular/core';
11
+ import { RouterLink } from '@angular/router';
12
+ import {
13
+ BfMenuButtonComponent,
14
+ BfMenuComponent,
15
+ BfMenuDividerDirective,
16
+ BfMenuGroupDirective,
17
+ BfMenuItemCheckboxDirective,
18
+ BfMenuItemDirective,
19
+ BfMenuItemRadioDirective,
20
+ BfMenuTriggerDirective,
21
+ } from '@bitforgehq/ui/overlay';
22
+
23
+ type Density = 'compact' | 'comfortable';
24
+
25
+ /**
26
+ * The bf-menu overview.
27
+ *
28
+ * Live demo of `bf-menu`: row actions with a "Move to" submenu, a disabled item, a link item that
29
+ * navigates to the `bf-menu-button` page (closing the menu) and a destructive item after a divider, then a view menu with a "Show grid" checkbox item and a density radio
30
+ * group; the result line reports each choice. The triggers and the result line carry
31
+ * `data-testid`s for the end-to-end suite.
32
+ */
33
+ @Component({
34
+ selector: 'app-example-menu-overview',
35
+ imports: [
36
+ BfMenuButtonComponent,
37
+ BfMenuComponent,
38
+ BfMenuDividerDirective,
39
+ BfMenuGroupDirective,
40
+ BfMenuItemCheckboxDirective,
41
+ BfMenuItemDirective,
42
+ BfMenuItemRadioDirective,
43
+ BfMenuTriggerDirective,
44
+ RouterLink,
45
+ ],
46
+ template: `
47
+ <div class="flex flex-col gap-4">
48
+ <div class="flex flex-wrap items-center gap-2">
49
+ <bf-menu-button data-testid="menu-actions" [menu]="rowActions">Actions</bf-menu-button>
50
+ <bf-menu-button data-testid="menu-view" [menu]="view" variant="text">View</bf-menu-button>
51
+ </div>
52
+ <p
53
+ data-status
54
+ data-testid="menu-result"
55
+ class="text-caption text-text-muted"
56
+ aria-live="polite"
57
+ >
58
+ {{ result() }}
59
+ </p>
60
+ </div>
61
+
62
+ <ng-template #rowActions>
63
+ <bf-menu>
64
+ <button bfMenuItem (triggered)="result.set('Renamed Atlas rebrand.')">Rename</button>
65
+ <button bfMenuItem (triggered)="result.set('Duplicated Atlas rebrand.')">Duplicate</button>
66
+ <button bfMenuItem [bfMenuTriggerFor]="moveTo">Move to</button>
67
+ <button bfMenuItem bfMenuItemDisabled>Share</button>
68
+ <a bfMenuItem routerLink="/components/menu-button">Open the menu button page</a>
69
+ <div bfMenuDivider></div>
70
+ <button bfMenuItem (triggered)="result.set('Deleted (not really, this is a demo).')">
71
+ Delete
72
+ </button>
73
+ </bf-menu>
74
+ </ng-template>
75
+
76
+ <ng-template #moveTo>
77
+ <bf-menu>
78
+ <button bfMenuItem (triggered)="result.set('Moved to Inbox.')">Inbox</button>
79
+ <button bfMenuItem (triggered)="result.set('Moved to In progress.')">In progress</button>
80
+ <button bfMenuItem (triggered)="result.set('Moved to Done.')">Done</button>
81
+ </bf-menu>
82
+ </ng-template>
83
+
84
+ <ng-template #view>
85
+ <bf-menu>
86
+ <button bfMenuItemCheckbox [(checked)]="showGrid" (changed)="reportGrid($event)">
87
+ Show grid
88
+ </button>
89
+ <div bfMenuDivider></div>
90
+ <div bfMenuGroup aria-label="Density">
91
+ <button
92
+ bfMenuItemRadio
93
+ [checked]="density() === 'compact'"
94
+ (triggered)="setDensity('compact')"
95
+ >
96
+ Compact
97
+ </button>
98
+ <button
99
+ bfMenuItemRadio
100
+ [checked]="density() === 'comfortable'"
101
+ (triggered)="setDensity('comfortable')"
102
+ >
103
+ Comfortable
104
+ </button>
105
+ </div>
106
+ </bf-menu>
107
+ </ng-template>
108
+ `,
109
+ })
110
+ export class MenuOverviewExampleComponent {
111
+ readonly result = signal('');
112
+ readonly showGrid = signal(false);
113
+ readonly density = signal<Density>('compact');
114
+
115
+ reportGrid(shown: boolean): void {
116
+ this.result.set(shown ? 'Grid shown.' : 'Grid hidden.');
117
+ }
118
+
119
+ setDensity(density: Density): void {
120
+ this.density.set(density);
121
+ this.result.set(density === 'compact' ? 'Compact rows.' : 'Comfortable rows.');
122
+ }
123
+ }
124
+ ```
125
+
126
+ ## Overview
127
+
128
+ Menu panel of actions, choices and submenus on the Angular CDK menu (role `menu`, arrow keys,
129
+ Home/End, type-ahead, `Escape` returning focus to the trigger). Put it in an `ng-template` that
130
+ a `bf-menu-button` or a `[bfMenuTriggerFor]` opens, and fill it with `bfMenuItem`,
131
+ `bfMenuItemCheckbox`, `bfMenuItemRadio` (in a `bfMenuGroup`) and `bfMenuDivider` parts.
132
+
133
+ Menus of actions and choices for desktop and web apps, from `@bitforgehq/ui/overlay` (needs
134
+ `@angular/cdk`, no Capacitor). A `bf-menu` is the panel; put it in an `ng-template` and open it
135
+ from a `bf-menu-button` or from any element with `[bfMenuTriggerFor]`. Fill it with item parts:
136
+ `bfMenuItem` (an action or a link), `bfMenuItemCheckbox` (a toggle), `bfMenuItemRadio` (one
137
+ choice of a set, inside a `bfMenuGroup`) and `bfMenuDivider`. The menu is built on the Angular
138
+ CDK menu, which provides the roles and the keyboard behaviour of the
139
+ [APG menu button](https://www.w3.org/WAI/ARIA/apg/patterns/menu-button/) pattern. Menus open in
140
+ the CDK overlay container, so they work inside a `BfDialogService` dialog.
141
+
142
+ Import the parts you use from `@bitforgehq/ui/overlay`: `BfMenuButtonComponent`, `BfMenuComponent`,
143
+ `BfMenuDividerDirective`, `BfMenuGroupDirective`, `BfMenuItemCheckboxDirective`,
144
+ `BfMenuItemDirective`, `BfMenuItemRadioDirective` and `BfMenuTriggerDirective`.
145
+
146
+ ## When to use
147
+
148
+ Use `bf-menu` for a short list of actions or view settings that opens from a button: Rename, Move to, Delete; Show grid, Density. Use [bf-select](https://bitforge-ui.web.app/components/select) instead to choose a form value, [BfDialogService](https://bitforge-ui.web.app/api/bf-dialog-service) when the choice needs fields or explanation, links in a `nav` for site navigation, and [BfActionSheetService](https://bitforge-ui.web.app/api/bf-action-sheet-service) in a Capacitor app.
149
+
150
+ ## Basic usage
151
+
152
+ Items are native `<button>` elements. `triggered` fires when one is activated with a click,
153
+ `Enter` or `Space`; the menu then closes and focus returns to the button that opened it.
154
+
155
+ ```ts
156
+ import { Component, signal } from '@angular/core';
157
+ import {
158
+ BfMenuButtonComponent,
159
+ BfMenuComponent,
160
+ BfMenuItemDirective,
161
+ } from '@bitforgehq/ui/overlay';
162
+
163
+ /**
164
+ * Row actions in a menu.
165
+ *
166
+ * A `bf-menu-button` opens the menu; each item reports its `triggered`, and Archive is disabled.
167
+ */
168
+ @Component({
169
+ selector: 'app-example-menu-basic',
170
+ imports: [BfMenuButtonComponent, BfMenuComponent, BfMenuItemDirective],
171
+ template: `
172
+ <bf-menu-button [menu]="rowActions">Actions</bf-menu-button>
173
+ <p aria-live="polite" class="text-caption text-text-muted mt-3">{{ result() }}</p>
174
+
175
+ <ng-template #rowActions>
176
+ <bf-menu>
177
+ <button bfMenuItem (triggered)="result.set('Renamed.')">Rename</button>
178
+ <button bfMenuItem (triggered)="result.set('Duplicated.')">Duplicate</button>
179
+ <button bfMenuItem bfMenuItemDisabled>Archive</button>
180
+ </bf-menu>
181
+ </ng-template>
182
+ `,
183
+ })
184
+ export class MenuBasicExampleComponent {
185
+ protected readonly result = signal('');
186
+ }
187
+ ```
188
+
189
+ `bfMenuItemDisabled` disables an item. It stays focusable and is announced as disabled
190
+ (`aria-disabled`), but it does not activate. Put icons inside the item before the label; the
191
+ item lays them out with a 12 px gap. `bfMenuItemTypeahead` sets the text that type-ahead matches
192
+ when the label starts with something else (an emoji, a count).
193
+
194
+ Any element can open a menu with `[bfMenuTriggerFor]`, for example your own toolbar button.
195
+ `(bfMenuOpened)` and `(bfMenuClosed)` report its state, and `bfMenuTriggerData` passes a context
196
+ to the template.
197
+
198
+ ```ts
199
+ import { Component, signal } from '@angular/core';
200
+ import { BfIconComponent } from '@bitforgehq/ui/icons';
201
+ import {
202
+ BfMenuComponent,
203
+ BfMenuItemDirective,
204
+ BfMenuTriggerDirective,
205
+ } from '@bitforgehq/ui/overlay';
206
+ import { Settings2 } from 'lucide';
207
+
208
+ /**
209
+ * A menu opened from your own button.
210
+ *
211
+ * `[bfMenuTriggerFor]` turns any element into the trigger; `bfMenuClosed` reports when the menu closes.
212
+ */
213
+ @Component({
214
+ selector: 'app-example-menu-trigger',
215
+ imports: [BfIconComponent, BfMenuComponent, BfMenuItemDirective, BfMenuTriggerDirective],
216
+ template: `
217
+ <button
218
+ type="button"
219
+ class="border-border-strong text-text rounded-control inline-flex h-10 items-center gap-2 border px-4"
220
+ [bfMenuTriggerFor]="options"
221
+ (bfMenuClosed)="result.set('Layout saved.')"
222
+ >
223
+ <bf-icon [icon]="settings" size="sm" />
224
+ Options
225
+ </button>
226
+ <p aria-live="polite" class="text-caption text-text-muted mt-3">{{ result() }}</p>
227
+
228
+ <ng-template #options>
229
+ <bf-menu>
230
+ <button bfMenuItem (triggered)="result.set('Pinned.')">Pin</button>
231
+ <button bfMenuItem (triggered)="result.set('Hidden.')">Hide</button>
232
+ </bf-menu>
233
+ </ng-template>
234
+ `,
235
+ })
236
+ export class MenuTriggerExampleComponent {
237
+ protected readonly settings = Settings2;
238
+ protected readonly result = signal('');
239
+ }
240
+ ```
241
+
242
+ ## Groups and dividers
243
+
244
+ `bfMenuDivider` draws a line between groups of items (role `separator`, skipped by the arrow
245
+ keys). `bfMenuGroup` groups items (role `group`). Name a group with `aria-label` when its items
246
+ share a topic.
247
+
248
+ ```ts
249
+ import { Component, signal } from '@angular/core';
250
+ import {
251
+ BfMenuButtonComponent,
252
+ BfMenuComponent,
253
+ BfMenuDividerDirective,
254
+ BfMenuItemDirective,
255
+ } from '@bitforgehq/ui/overlay';
256
+
257
+ /**
258
+ * A menu with a divider before the destructive item.
259
+ *
260
+ * Items in reading order: the clipboard actions, a `bfMenuDivider`, then Delete.
261
+ */
262
+ @Component({
263
+ selector: 'app-example-menu-groups',
264
+ imports: [BfMenuButtonComponent, BfMenuComponent, BfMenuDividerDirective, BfMenuItemDirective],
265
+ template: `
266
+ <bf-menu-button [menu]="edit" variant="text">Edit</bf-menu-button>
267
+ <p aria-live="polite" class="text-caption text-text-muted mt-3">{{ result() }}</p>
268
+
269
+ <ng-template #edit>
270
+ <bf-menu>
271
+ <button bfMenuItem (triggered)="result.set('Cut.')">Cut</button>
272
+ <button bfMenuItem (triggered)="result.set('Copied.')">Copy</button>
273
+ <div bfMenuDivider></div>
274
+ <button bfMenuItem (triggered)="result.set('Deleted.')">Delete</button>
275
+ </bf-menu>
276
+ </ng-template>
277
+ `,
278
+ })
279
+ export class MenuGroupsExampleComponent {
280
+ protected readonly result = signal('');
281
+ }
282
+ ```
283
+
284
+ ## Checkbox and radio items
285
+
286
+ `bfMenuItemCheckbox` is a toggle (role `menuitemcheckbox`) with a check mark while `checked`.
287
+ `bfMenuItemRadio` is one choice of a set (role `menuitemradio`) with a dot while `checked`. Radio
288
+ items in the same `bfMenuGroup` are exclusive. Activating either one closes the menu.
289
+
290
+ A checkbox item flips its own `checked` when activated and emits `changed` (and the model's
291
+ `checkedChange`) with the new state, so bind it two-way, `[(checked)]="showGrid"`, and react in
292
+ `(changed)`. With a one-way `[checked]` binding, always
293
+ update your state in `(triggered)`: if you skip the update (a guarded toggle), the item's
294
+ `aria-checked` no longer matches your state. For radio items, bind `[checked]` and set your state
295
+ in `(triggered)`.
296
+
297
+ ```ts
298
+ import { Component, signal } from '@angular/core';
299
+ import {
300
+ BfMenuButtonComponent,
301
+ BfMenuComponent,
302
+ BfMenuDividerDirective,
303
+ BfMenuGroupDirective,
304
+ BfMenuItemCheckboxDirective,
305
+ BfMenuItemRadioDirective,
306
+ } from '@bitforgehq/ui/overlay';
307
+
308
+ type Density = 'compact' | 'comfortable';
309
+
310
+ /**
311
+ * Checkbox and radio items.
312
+ *
313
+ * "Show grid" is a two-way bound checkbox item; the density radio items share one `bfMenuGroup`.
314
+ */
315
+ @Component({
316
+ selector: 'app-example-menu-checkable',
317
+ imports: [
318
+ BfMenuButtonComponent,
319
+ BfMenuComponent,
320
+ BfMenuDividerDirective,
321
+ BfMenuGroupDirective,
322
+ BfMenuItemCheckboxDirective,
323
+ BfMenuItemRadioDirective,
324
+ ],
325
+ template: `
326
+ <bf-menu-button [menu]="view" variant="text">View</bf-menu-button>
327
+ <p class="text-caption text-text-muted mt-3">
328
+ Grid {{ showGrid() ? 'on' : 'off' }} · {{ density() }}
329
+ </p>
330
+
331
+ <ng-template #view>
332
+ <bf-menu>
333
+ <button bfMenuItemCheckbox [(checked)]="showGrid">Show grid</button>
334
+ <div bfMenuDivider></div>
335
+ <div bfMenuGroup aria-label="Density">
336
+ <button
337
+ bfMenuItemRadio
338
+ [checked]="density() === 'compact'"
339
+ (triggered)="density.set('compact')"
340
+ >
341
+ Compact
342
+ </button>
343
+ <button
344
+ bfMenuItemRadio
345
+ [checked]="density() === 'comfortable'"
346
+ (triggered)="density.set('comfortable')"
347
+ >
348
+ Comfortable
349
+ </button>
350
+ </div>
351
+ </bf-menu>
352
+ </ng-template>
353
+ `,
354
+ })
355
+ export class MenuCheckableExampleComponent {
356
+ protected readonly showGrid = signal(true);
357
+ protected readonly density = signal<Density>('comfortable');
358
+ }
359
+ ```
360
+
361
+ ## Submenus
362
+
363
+ Put `[bfMenuTriggerFor]` on a `bfMenuItem` to open another `bf-menu` from it. The item shows a
364
+ chevron at the inline end and gets `aria-haspopup="menu"` and `aria-expanded`. The submenu opens
365
+ beside the item, at its inline end (to the left in right-to-left pages), and flips when there is
366
+ no room. `ArrowRight` (`ArrowLeft` in RTL), `Enter`, `Space` or the pointer open it. `ArrowLeft`
367
+ (`ArrowRight` in RTL) or `Escape` close it and put focus back on the item. Activating an item in
368
+ a submenu closes every menu.
369
+
370
+ ```ts
371
+ import { Component, signal } from '@angular/core';
372
+ import {
373
+ BfMenuButtonComponent,
374
+ BfMenuComponent,
375
+ BfMenuItemDirective,
376
+ BfMenuTriggerDirective,
377
+ } from '@bitforgehq/ui/overlay';
378
+
379
+ /**
380
+ * A menu item that opens a submenu.
381
+ *
382
+ * An item with `[bfMenuTriggerFor]` opens the next menu beside it (ArrowRight opens it too).
383
+ */
384
+ @Component({
385
+ selector: 'app-example-menu-submenus',
386
+ imports: [BfMenuButtonComponent, BfMenuComponent, BfMenuItemDirective, BfMenuTriggerDirective],
387
+ template: `
388
+ <bf-menu-button [menu]="rowActions">Actions</bf-menu-button>
389
+ <p aria-live="polite" class="text-caption text-text-muted mt-3">{{ result() }}</p>
390
+
391
+ <ng-template #rowActions>
392
+ <bf-menu>
393
+ <button bfMenuItem (triggered)="result.set('Renamed.')">Rename</button>
394
+ <button bfMenuItem [bfMenuTriggerFor]="moveTo">Move to</button>
395
+ </bf-menu>
396
+ </ng-template>
397
+
398
+ <ng-template #moveTo>
399
+ <bf-menu>
400
+ <button bfMenuItem (triggered)="result.set('Moved to Inbox.')">Inbox</button>
401
+ <button bfMenuItem (triggered)="result.set('Moved to Archive.')">Archive</button>
402
+ </bf-menu>
403
+ </ng-template>
404
+ `,
405
+ })
406
+ export class MenuSubmenusExampleComponent {
407
+ protected readonly result = signal('');
408
+ }
409
+ ```
410
+
411
+ ## Links as items
412
+
413
+ A navigation item is a real link: put `bfMenuItem` on an `<a>` with `href`, or with your app's
414
+ `routerLink` (the library itself does not depend on the router). It keeps its link semantics for
415
+ the browser (open in a new tab, the status bar URL) and gets role `menuitem`. `Enter` and a click
416
+ follow it, and so does `Space`, which a plain link ignores. Activating an item closes the menu;
417
+ any navigation to another URL path (a link item, the browser's back button, a redirect) closes an
418
+ open menu too, while a query- or fragment-only change leaves it open. A disabled link item
419
+ (`bfMenuItemDisabled`) does not navigate on a click or `Enter`, with `href` or `routerLink`.
420
+
421
+ ```ts
422
+ import { Component } from '@angular/core';
423
+ import { RouterLink } from '@angular/router';
424
+ import {
425
+ BfMenuButtonComponent,
426
+ BfMenuComponent,
427
+ BfMenuItemDirective,
428
+ } from '@bitforgehq/ui/overlay';
429
+
430
+ /** Links as menu items: a router link and an external link. */
431
+ @Component({
432
+ selector: 'app-example-menu-links',
433
+ imports: [RouterLink, BfMenuButtonComponent, BfMenuComponent, BfMenuItemDirective],
434
+ template: `
435
+ <bf-menu-button [menu]="help" variant="text">Help</bf-menu-button>
436
+
437
+ <ng-template #help>
438
+ <bf-menu>
439
+ <a bfMenuItem routerLink="/components/menu-button">Menu button docs</a>
440
+ <a bfMenuItem href="https://bitforge.ch">Bitforge</a>
441
+ </bf-menu>
442
+ </ng-template>
443
+ `,
444
+ })
445
+ export class MenuLinksExampleComponent {}
446
+ ```
447
+
448
+ ## Placement
449
+
450
+ `bfMenuPlacement` on a trigger (`placement` on `bf-menu-button`) sets where the menu opens:
451
+ `'below-start'` (default), `'below-end'`, `'above-start'` or `'above-end'`. `start` and `end`
452
+ follow the text direction. When the menu would overflow the viewport, it flips to the other
453
+ side vertically first, then to the other alignment. The panel is at least 12 rem wide and
454
+ scrolls past 24 rem or the viewport height minus 2 rem.
455
+
456
+ ```ts
457
+ import { Component, signal } from '@angular/core';
458
+ import { BfIconComponent } from '@bitforgehq/ui/icons';
459
+ import {
460
+ BfMenuComponent,
461
+ BfMenuItemDirective,
462
+ BfMenuTriggerDirective,
463
+ } from '@bitforgehq/ui/overlay';
464
+ import { Columns3 } from 'lucide';
465
+
466
+ /**
467
+ * A menu aligned to the trigger's end.
468
+ *
469
+ * `bfMenuPlacement="below-end"` opens the menu under the trigger, aligned to its inline end.
470
+ */
471
+ @Component({
472
+ selector: 'app-example-menu-placement',
473
+ imports: [BfIconComponent, BfMenuComponent, BfMenuItemDirective, BfMenuTriggerDirective],
474
+ template: `
475
+ <div class="flex justify-end">
476
+ <button
477
+ type="button"
478
+ class="border-border-strong text-text rounded-control inline-flex h-10 items-center gap-2 border px-4"
479
+ [bfMenuTriggerFor]="columns"
480
+ bfMenuPlacement="below-end"
481
+ >
482
+ <bf-icon [icon]="columnsIcon" size="sm" />
483
+ Columns
484
+ </button>
485
+ </div>
486
+ <p aria-live="polite" class="text-caption text-text-muted mt-3">{{ result() }}</p>
487
+
488
+ <ng-template #columns>
489
+ <bf-menu>
490
+ <button bfMenuItem (triggered)="result.set('Owner column shown.')">Owner</button>
491
+ <button bfMenuItem (triggered)="result.set('Due date column shown.')">Due date</button>
492
+ </bf-menu>
493
+ </ng-template>
494
+ `,
495
+ })
496
+ export class MenuPlacementExampleComponent {
497
+ protected readonly columnsIcon = Columns3;
498
+ protected readonly result = signal('');
499
+ }
500
+ ```
501
+
502
+ ## Sizes
503
+
504
+ Items are 32 (`sm`), 36 (`md`) or 44 px (`lg`) tall. Unset, `size` follows `provideBfDefaults()`
505
+ (`controlSize`, `md` by default). Under a coarse pointer (touch) `sm` and `md` rows grow to 44 px.
506
+
507
+ ```ts
508
+ import { Component, signal } from '@angular/core';
509
+ import {
510
+ BfMenuButtonComponent,
511
+ BfMenuComponent,
512
+ BfMenuItemDirective,
513
+ } from '@bitforgehq/ui/overlay';
514
+
515
+ /**
516
+ * A small menu for a dense toolbar.
517
+ *
518
+ * `size="sm"` sets the item height to 32 px, independent of the trigger's size.
519
+ */
520
+ @Component({
521
+ selector: 'app-example-menu-sizes',
522
+ imports: [BfMenuButtonComponent, BfMenuComponent, BfMenuItemDirective],
523
+ template: `
524
+ <bf-menu-button [menu]="sort" size="sm" variant="text">Sort</bf-menu-button>
525
+ <p aria-live="polite" class="text-caption text-text-muted mt-3">{{ result() }}</p>
526
+
527
+ <ng-template #sort>
528
+ <bf-menu size="sm">
529
+ <button bfMenuItem (triggered)="result.set('Sorted by name.')">Name</button>
530
+ <button bfMenuItem (triggered)="result.set('Sorted by date.')">Date</button>
531
+ </bf-menu>
532
+ </ng-template>
533
+ `,
534
+ })
535
+ export class MenuSizesExampleComponent {
536
+ protected readonly result = signal('');
537
+ }
538
+ ```
539
+
540
+ ## Desktop
541
+
542
+ Built for desktop: hovering an item gives it the `surface-muted` fill and the pointer cursor (a disabled item shows `not-allowed` and no fill), and the focus ring is added for keyboard focus only. A submenu opens on hover and stays open while the pointer moves diagonally towards it. Items are 36 px (`md`) by default, 32 px at `sm` for dense apps, and 44 px under a coarse pointer.
543
+
544
+ ## Theming
545
+
546
+ - Panel: `bg-surface`, `text-text`, a 1 px `border-border`, `rounded-card`, `shadow-floating`,
547
+ `p-1`.
548
+ - Item: `rounded-field`, `text-body` (`text-label-sm` at `sm`); hover and focus
549
+ `bg-surface-muted`, keyboard focus adds the `ring-ring` focus ring; disabled `text-disabled`.
550
+ - Divider: a 1 px `border-border` line. The check mark, the radio dot and the submenu chevron are
551
+ drawn in the item's text colour.
552
+
553
+ ## Accessibility
554
+
555
+ - Roles: the panel is `role="menu"` (vertical). Items are `menuitem`, `menuitemcheckbox` or
556
+ `menuitemradio` with `aria-checked`, groups are `group` and dividers `separator`. The trigger
557
+ has `aria-haspopup="menu"`, `aria-expanded` and, while open, `aria-controls`. A menu is named
558
+ by the trigger that opened it (`aria-labelledby`; the trigger gets an id when it has none,
559
+ read each time the menu opens) unless you set `ariaLabel` or `ariaLabelledby`. A submenu is
560
+ named by its parent item.
561
+ - The check mark, the dot and the chevron are CSS drawings: they add nothing to an item's
562
+ accessible name, and they stay visible under forced colours (Windows High Contrast), where the
563
+ focused item is drawn in `Highlight` (a focused disabled item in `GrayText` with a `Highlight`
564
+ outline).
565
+ - Disabled items stay focusable, so screen-reader users find them, and announce
566
+ `aria-disabled="true"` (APG). They do not activate.
567
+ - Focus: opening the menu moves focus to its first item (to the last with `ArrowUp`). Closing it
568
+ with `Escape`, `Tab` or an item returns focus to the trigger. The items are not in the page's
569
+ tab order; `Tab` leaves the menu. A menu opened inside a dialog closes on `Escape` without
570
+ closing the dialog.
571
+ - Pointer: rows are at least 32 px tall, 44 px under a coarse pointer. An open submenu stays open
572
+ while the pointer moves diagonally towards it across other items (CDK menu aim).
573
+
574
+ ### Keyboard
575
+
576
+ From the [APG menu button](https://www.w3.org/WAI/ARIA/apg/patterns/menu-button/) and
577
+ [menu](https://www.w3.org/WAI/ARIA/apg/patterns/menubar/) patterns:
578
+
579
+ | Key | Where | Action |
580
+ | ---------------------------------------------------------- | --------------- | --------------------------------------------------------------- |
581
+ | <kbd>Enter</kbd> / <kbd>Space</kbd> / <kbd>ArrowDown</kbd> | Trigger | Opens the menu and focuses the first item |
582
+ | <kbd>ArrowUp</kbd> | Trigger | Opens the menu and focuses the last item |
583
+ | <kbd>ArrowDown</kbd> / <kbd>ArrowUp</kbd> | Menu | Next / previous item, wrapping at the ends |
584
+ | <kbd>Home</kbd> / <kbd>End</kbd> | Menu | First / last item |
585
+ | <kbd>A</kbd>–<kbd>Z</kbd>, <kbd>0</kbd>–<kbd>9</kbd> | Menu | Next item whose label starts with the typed text |
586
+ | <kbd>Enter</kbd> / <kbd>Space</kbd> | Item | Activates the item (toggles a checkbox, picks a radio), closes |
587
+ | <kbd>ArrowRight</kbd> (RTL <kbd>ArrowLeft</kbd>) | Submenu item | Opens the submenu and focuses its first item |
588
+ | <kbd>ArrowLeft</kbd> (RTL <kbd>ArrowRight</kbd>) | Submenu | Closes the submenu, focus back on its item |
589
+ | <kbd>Escape</kbd> | Menu or submenu | Closes it; focus back on the trigger (or the parent item) |
590
+ | <kbd>Tab</kbd> | Menu | Closes every menu, focus back on the trigger, then Tab moves on |
591
+
592
+ ## Inputs
593
+
594
+ | Name | Type | Default | Description |
595
+ | ---------------- | ----------------------------------------------------------- | ----------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
596
+ | `size` | `BfSize \| undefined` (`'sm' \| 'md' \| 'lg' \| undefined`) | `undefined` | Row height of its items: sm 32, md 36, lg 44 px (44 px under a coarse pointer); unset follows `provideBfDefaults()` (`controlSize`, `md` by default). |
597
+ | `ariaLabel` | `string` | `''` | Accessible name of the menu. Empty: the menu is named by the trigger that opened it (APG). |
598
+ | `ariaLabelledby` | `string` | `''` | Id of the element that names the menu; wins over the trigger. |
599
+
600
+ ## Slots
601
+
602
+ - `default`: The items (`bfMenuItem`, `bfMenuItemCheckbox`, `bfMenuItemRadio`), groups and dividers.
603
+
604
+ ## Notes
605
+
606
+ ### Do
607
+
608
+ - Word items as verbs or destinations ("Rename", "Move to", "Settings") and keep the list short.
609
+ - Put destructive items last, after a divider.
610
+ - Use checkbox and radio items for view settings that apply at once ("Show grid", "Density").
611
+ - Use `<a bfMenuItem>` for items that navigate, so they are real links.
612
+
613
+ ### Don't
614
+
615
+ - Don't put form fields, sliders or other widgets inside a menu; use a dialog.
616
+ - Don't nest submenus more than one level deep.
617
+ - Don't hide the only way to do something in a menu; frequent actions belong in the toolbar.
618
+ - Don't use a menu for site navigation (a list of pages); use links in a `nav`.
619
+
620
+ ## Migration notes
621
+
622
+ ### 0.3.0
623
+
624
+ New `changed` output on `bfMenuItemCheckbox`: the new `checked` state right after the user toggles
625
+ the item (after `triggered`, beside the model's `checkedChange`), so it has the one `changed` of
626
+ every value control.
627
+
628
+ New `bf-menu` and its parts in `@bitforgehq/ui/overlay`, on the Angular CDK menu (APG menu button
629
+ and menu patterns): `bfMenuItem` on a `<button>` or a link (`<a>` with `href` or `routerLink`),
630
+ `bfMenuItemCheckbox` and `bfMenuItemRadio` (`checked`, CSS-drawn check mark and dot),
631
+ `bfMenuGroup` (radio items exclusive), `bfMenuDivider` and `[bfMenuTriggerFor]` (on any element,
632
+ or on an item for a submenu with a CSS chevron, mirrored in RTL). Items take `bfMenuItemDisabled`
633
+ (focusable, `aria-disabled`) and `bfMenuItemTypeahead` and emit `triggered`; the trigger takes
634
+ `bfMenuTriggerData` and `bfMenuPlacement` (`below-start` (default), `below-end`, `above-start`,
635
+ `above-end`, flipping on overflow; submenus open beside their item) and emits `bfMenuOpened` and
636
+ `bfMenuClosed`. `bf-menu` takes `size` (item rows `sm` 32, `md` 36, `lg` 44 px, 44 px under a
637
+ coarse pointer; unset follows `provideBfDefaults()` `controlSize`), `ariaLabel` and
638
+ `ariaLabelledby`; without either it is named by its trigger. Keyboard: `Enter`, `Space`,
639
+ `ArrowDown` open on the first item, `ArrowUp` on the last; arrows wrap, `Home`/`End`, type-ahead,
640
+ `Escape` and `Tab` close with focus back on the trigger, `ArrowRight`/`ArrowLeft` open and close
641
+ submenus (swapped in RTL); `Space` also follows a link item (once, ignoring auto-repeat), and a
642
+ disabled link item never navigates, also with `routerLink`. `bfMenuItemCheckbox` emits
643
+ `checkedChange` with the state it flipped to, so `[(checked)]` binds it two-way. An open submenu
644
+ stays open while the pointer moves diagonally towards it (CDK menu aim). Under forced colours the
645
+ focused item is drawn in `Highlight`, a focused disabled one in `GrayText` on `Canvas` with a
646
+ `Highlight` outline. A navigation to another URL path closes an open menu (query- and
647
+ fragment-only changes do not); the trigger watches the URL only while its menu is open.
648
+
649
+ JSDoc of `menu-item.directive.ts` for the API reference: `@summary`/`@since` on the exported
650
+ symbols; `@binding` docs for the host-directive bindings `bfMenuItemDisabled`,
651
+ `bfMenuItemTypeahead`, `triggered`, `checked`. No behaviour change.
652
+
653
+ JSDoc of `menu-trigger.directive.ts` for the API reference: `@summary`/`@since` on the exported
654
+ symbols; `@binding` docs for the host-directive bindings `bfMenuTriggerFor`, `bfMenuTriggerData`,
655
+ `bfMenuOpened`, `bfMenuClosed`. No behaviour change.
656
+
657
+ JSDoc of `menu.component.ts` for the API reference: `@slot` docs for `default`. No behaviour change.
658
+
659
+ The two effects that write into the CDK host directives (`bfMenuItemCheckbox`'s `checked`,
660
+ `bfMenuTrigger`'s `menuPosition` and `ngOnChanges`) carry an `effect-justified` comment: the CDK
661
+ exposes no input they could derive from. No behaviour change.