@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,377 @@
1
+ # bf-menu-button
2
+
3
+ Styled button that opens a menu, with outline, text, tonal and icon-only looks
4
+
5
+ `BfMenuButtonComponent` from `@bitforgehq/ui/overlay` · Overlays · since 0.3.0
6
+
7
+ ## Example
8
+
9
+ ```ts
10
+ import { Component, signal } from '@angular/core';
11
+ import { BfIconComponent } from '@bitforgehq/ui/icons';
12
+ import {
13
+ BfMenuButtonComponent,
14
+ BfMenuComponent,
15
+ BfMenuItemDirective,
16
+ } from '@bitforgehq/ui/overlay';
17
+ import { Ellipsis } from 'lucide';
18
+
19
+ /**
20
+ * The bf-menu-button overview.
21
+ *
22
+ * Live demo of `bf-menu-button`: the outline and text variants and an icon-only "More actions"
23
+ * button, all opening the same menu; the result line reports the chosen item. The triggers and
24
+ * the result line carry `data-testid`s for the end-to-end suite.
25
+ */
26
+ @Component({
27
+ selector: 'app-example-menu-button-overview',
28
+ imports: [BfIconComponent, BfMenuButtonComponent, BfMenuComponent, BfMenuItemDirective],
29
+ template: `
30
+ <div class="flex flex-col gap-4">
31
+ <div class="flex flex-wrap items-center gap-2">
32
+ <bf-menu-button data-testid="menu-button-export" [menu]="exportMenu">Export</bf-menu-button>
33
+ <bf-menu-button data-testid="menu-button-export-text" [menu]="exportMenu" variant="text"
34
+ >Export</bf-menu-button
35
+ >
36
+ <bf-menu-button
37
+ data-testid="menu-button-more"
38
+ [menu]="exportMenu"
39
+ iconOnly
40
+ label="More actions"
41
+ variant="text"
42
+ placement="below-end"
43
+ >
44
+ <bf-icon [icon]="ellipsis" />
45
+ </bf-menu-button>
46
+ </div>
47
+ <p
48
+ data-status
49
+ data-testid="menu-button-result"
50
+ class="text-caption text-text-muted"
51
+ aria-live="polite"
52
+ >
53
+ {{ result() }}
54
+ </p>
55
+ </div>
56
+
57
+ <ng-template #exportMenu>
58
+ <bf-menu>
59
+ <button bfMenuItem (triggered)="result.set('Exported as CSV.')">Export as CSV</button>
60
+ <button bfMenuItem (triggered)="result.set('Exported as PDF.')">Export as PDF</button>
61
+ <button bfMenuItem (triggered)="result.set('Link copied.')">Copy link</button>
62
+ </bf-menu>
63
+ </ng-template>
64
+ `,
65
+ })
66
+ export class MenuButtonOverviewExampleComponent {
67
+ readonly ellipsis = Ellipsis;
68
+ readonly result = signal('');
69
+ }
70
+ ```
71
+
72
+ ## Overview
73
+
74
+ Button that opens a `bf-menu`: a native `<button>` styled like `bf-button` (or `bf-icon-button`
75
+ with `iconOnly`) in the outline, text or tonal variant, with `aria-haspopup="menu"` and
76
+ `aria-expanded`. Click, `Enter`, `Space` or `ArrowDown` open the menu with its first item
77
+ focused, `ArrowUp` with its last; closing it returns focus here.
78
+
79
+ A button that opens a `bf-menu`, from `@bitforgehq/ui/overlay` (needs `@angular/cdk`, no
80
+ Capacitor). It renders a native `<button>` that looks like a `bf-button` (or a `bf-icon-button`
81
+ with `iconOnly`) in the outline, text or tonal variant, with a caret after its label. Pass the
82
+ `ng-template` holding the menu as `menu`. The menu page covers the items, groups, submenus and
83
+ the keyboard. Import `BfMenuButtonComponent`, `BfMenuComponent` and `BfMenuItemDirective` from
84
+ `@bitforgehq/ui/overlay`.
85
+
86
+ ## When to use
87
+
88
+ Use `bf-menu-button` for a button that opens a [bf-menu](https://bitforge-ui.web.app/components/menu) of actions: Export, Sort, a "More" button on a table row. Use [bf-button](https://bitforge-ui.web.app/components/button) instead for a single action, and [bf-select](https://bitforge-ui.web.app/components/select) to pick a form value.
89
+
90
+ ## Basic usage
91
+
92
+ ```ts
93
+ import { Component, signal } from '@angular/core';
94
+ import {
95
+ BfMenuButtonComponent,
96
+ BfMenuComponent,
97
+ BfMenuItemDirective,
98
+ } from '@bitforgehq/ui/overlay';
99
+
100
+ /**
101
+ * An export menu button.
102
+ *
103
+ * The menu button passes its `ng-template` as `menu`; each format reports its export.
104
+ */
105
+ @Component({
106
+ selector: 'app-example-menu-button-basic',
107
+ imports: [BfMenuButtonComponent, BfMenuComponent, BfMenuItemDirective],
108
+ template: `
109
+ <bf-menu-button [menu]="exportMenu">Export</bf-menu-button>
110
+ <p aria-live="polite" class="text-caption text-text-muted mt-3">{{ result() }}</p>
111
+
112
+ <ng-template #exportMenu>
113
+ <bf-menu>
114
+ <button bfMenuItem (triggered)="result.set('Exported as CSV.')">CSV</button>
115
+ <button bfMenuItem (triggered)="result.set('Exported as PDF.')">PDF</button>
116
+ </bf-menu>
117
+ </ng-template>
118
+ `,
119
+ })
120
+ export class MenuButtonBasicExampleComponent {
121
+ protected readonly result = signal('');
122
+ }
123
+ ```
124
+
125
+ `opened` and `closed` fire as the menu opens and closes. `disabled` disables the button (the
126
+ menu cannot open). An icon goes in the `icon-start` slot.
127
+
128
+ ```ts
129
+ import { Component, signal } from '@angular/core';
130
+ import { BfIconComponent } from '@bitforgehq/ui/icons';
131
+ import {
132
+ BfMenuButtonComponent,
133
+ BfMenuComponent,
134
+ BfMenuItemDirective,
135
+ } from '@bitforgehq/ui/overlay';
136
+ import { Download } from 'lucide';
137
+
138
+ /**
139
+ * A tonal menu button with an icon.
140
+ *
141
+ * The icon goes in the icon-start slot; `closed` reports each time the menu closes.
142
+ */
143
+ @Component({
144
+ selector: 'app-example-menu-button-icon',
145
+ imports: [BfIconComponent, BfMenuButtonComponent, BfMenuComponent, BfMenuItemDirective],
146
+ template: `
147
+ <bf-menu-button [menu]="exportMenu" variant="tonal" (closed)="closes.set(closes() + 1)">
148
+ <bf-icon bfSlot="icon-start" [icon]="download" />
149
+ Export
150
+ </bf-menu-button>
151
+ <p class="text-caption text-text-muted mt-3">Closed {{ closes() }} times.</p>
152
+
153
+ <ng-template #exportMenu>
154
+ <bf-menu>
155
+ <button bfMenuItem>CSV</button>
156
+ <button bfMenuItem>PDF</button>
157
+ </bf-menu>
158
+ </ng-template>
159
+ `,
160
+ })
161
+ export class MenuButtonIconExampleComponent {
162
+ protected readonly download = Download;
163
+ protected readonly closes = signal(0);
164
+ }
165
+ ```
166
+
167
+ ## Variants
168
+
169
+ `outline` (default), `text` and `tonal`, with the same colours as `bf-button`. A menu button is
170
+ never `primary`: it opens a choice, it is not the page's main action.
171
+
172
+ ```ts
173
+ import { Component, signal } from '@angular/core';
174
+ import {
175
+ BfMenuButtonComponent,
176
+ BfMenuComponent,
177
+ BfMenuItemDirective,
178
+ } from '@bitforgehq/ui/overlay';
179
+
180
+ /**
181
+ * The outline, text and tonal menu buttons.
182
+ *
183
+ * Three triggers for one sort menu.
184
+ */
185
+ @Component({
186
+ selector: 'app-example-menu-button-variants',
187
+ imports: [BfMenuButtonComponent, BfMenuComponent, BfMenuItemDirective],
188
+ template: `
189
+ <div class="flex flex-wrap gap-2">
190
+ <bf-menu-button [menu]="sortMenu">Sort</bf-menu-button>
191
+ <bf-menu-button [menu]="sortMenu" variant="text">Sort</bf-menu-button>
192
+ <bf-menu-button [menu]="sortMenu" variant="tonal">Sort</bf-menu-button>
193
+ </div>
194
+ <p aria-live="polite" class="text-caption text-text-muted mt-3">{{ result() }}</p>
195
+
196
+ <ng-template #sortMenu>
197
+ <bf-menu>
198
+ <button bfMenuItem (triggered)="result.set('Sorted by name.')">Name</button>
199
+ <button bfMenuItem (triggered)="result.set('Sorted by date.')">Date</button>
200
+ </bf-menu>
201
+ </ng-template>
202
+ `,
203
+ })
204
+ export class MenuButtonVariantsExampleComponent {
205
+ protected readonly result = signal('');
206
+ }
207
+ ```
208
+
209
+ ## Icon only
210
+
211
+ `iconOnly` renders a round button that holds only the projected icon, like `bf-icon-button`, and
212
+ drops the caret. Set `label`: it is the button's accessible name, as the icon gives none. A
213
+ "More" button on a table row is the typical use.
214
+
215
+ ```ts
216
+ import { Component, signal } from '@angular/core';
217
+ import { BfIconComponent } from '@bitforgehq/ui/icons';
218
+ import {
219
+ BfMenuButtonComponent,
220
+ BfMenuComponent,
221
+ BfMenuItemDirective,
222
+ } from '@bitforgehq/ui/overlay';
223
+ import { Ellipsis } from 'lucide';
224
+
225
+ /** An icon-only menu button named by its label. */
226
+ @Component({
227
+ selector: 'app-example-menu-button-icon-only',
228
+ imports: [BfIconComponent, BfMenuButtonComponent, BfMenuComponent, BfMenuItemDirective],
229
+ template: `
230
+ <bf-menu-button [menu]="rowActions" iconOnly label="More actions" variant="text">
231
+ <bf-icon [icon]="ellipsis" />
232
+ </bf-menu-button>
233
+ <p aria-live="polite" class="text-caption text-text-muted mt-3">{{ result() }}</p>
234
+
235
+ <ng-template #rowActions>
236
+ <bf-menu>
237
+ <button bfMenuItem (triggered)="result.set('Renamed.')">Rename</button>
238
+ <button bfMenuItem (triggered)="result.set('Deleted.')">Delete</button>
239
+ </bf-menu>
240
+ </ng-template>
241
+ `,
242
+ })
243
+ export class MenuButtonIconOnlyExampleComponent {
244
+ protected readonly ellipsis = Ellipsis;
245
+ protected readonly result = signal('');
246
+ }
247
+ ```
248
+
249
+ ## Sizes and placement
250
+
251
+ `size` is `sm` 32, `md` 40 or `lg` 48 px; unset it follows `provideBfDefaults()` (`controlSize`,
252
+ `md` by default). Under a coarse pointer `sm` and `md` reach 44 px without changing their look.
253
+ `placement` sets where the menu opens: `'below-start'` (default), `'below-end'`,
254
+ `'above-start'` or `'above-end'`, flipping when it would overflow the viewport.
255
+
256
+ ```ts
257
+ import { Component, signal } from '@angular/core';
258
+ import {
259
+ BfMenuButtonComponent,
260
+ BfMenuComponent,
261
+ BfMenuItemDirective,
262
+ } from '@bitforgehq/ui/overlay';
263
+
264
+ /**
265
+ * A small menu button that opens below its end.
266
+ *
267
+ * `size="sm"` sizes the trigger; `placement="below-end"` aligns the menu to its inline end.
268
+ */
269
+ @Component({
270
+ selector: 'app-example-menu-button-sizes',
271
+ imports: [BfMenuButtonComponent, BfMenuComponent, BfMenuItemDirective],
272
+ template: `
273
+ <div class="flex justify-end">
274
+ <bf-menu-button [menu]="rowActions" size="sm" placement="below-end">Actions</bf-menu-button>
275
+ </div>
276
+ <p aria-live="polite" class="text-caption text-text-muted mt-3">{{ result() }}</p>
277
+
278
+ <ng-template #rowActions>
279
+ <bf-menu>
280
+ <button bfMenuItem (triggered)="result.set('Renamed.')">Rename</button>
281
+ <button bfMenuItem (triggered)="result.set('Archived.')">Archive</button>
282
+ </bf-menu>
283
+ </ng-template>
284
+ `,
285
+ })
286
+ export class MenuButtonSizesExampleComponent {
287
+ protected readonly result = signal('');
288
+ }
289
+ ```
290
+
291
+ ## Desktop
292
+
293
+ Hover and focus look like `bf-button` (or `bf-icon-button` with `iconOnly`): the fill, border or text darkens on hover with the pointer cursor, and the focus ring shows on keyboard focus only. A click opens the menu; a second click on the button closes it. Under a fine pointer the visible box is the hit area. Desktop density is the default `md` step; `provideBfDefaults({ size: 'sm' })`, or `{ size: 'sm' }` in a component's `providers`, makes a dense toolbar, table or sidebar ([density guide](https://bitforge-ui.web.app/density)).
294
+
295
+ ## Theming
296
+
297
+ - Colours, border, radius, heights and the focus ring are `bf-button`'s (`bf-icon-button`'s with
298
+ `iconOnly`): outline `bg-surface` with a 1.5 px `border-border-strong`, text
299
+ `text-primary-text` (icon-only `text-text-muted` with a `surface-muted` hover), tonal
300
+ `bg-primary-soft` / `text-primary-strong`; disabled `bg-surface-sunken` / `text-disabled`.
301
+ - Caret: a CSS chevron in the label's colour.
302
+
303
+ ## Accessibility
304
+
305
+ - A native `<button type="button">` with `aria-haspopup="menu"` and `aria-expanded`, and the
306
+ menu it opens is named by it. Its name is its label, or `label` (`aria-label`). With `iconOnly`,
307
+ `label` is required. With a visible label, leave `label` empty or start it with the visible text
308
+ ([WCAG 2.5.3 Label in Name](https://www.w3.org/WAI/WCAG22/Understanding/label-in-name)).
309
+ - Keyboard ([APG menu button](https://www.w3.org/WAI/ARIA/apg/patterns/menu-button/)): `Enter`,
310
+ `Space` or `ArrowDown` open the menu with focus on the first item, `ArrowUp` with focus on the
311
+ last. Closing the menu (`Escape`, `Tab`, an item) returns focus to the button.
312
+ - The caret is drawn in CSS and adds nothing to the name.
313
+ - Hit area: at least 32 px, 44 px under a coarse pointer (as `bf-button`).
314
+
315
+ ### Keyboard
316
+
317
+ The keys inside the open menu are on the [bf-menu](https://bitforge-ui.web.app/components/menu#keyboard) page.
318
+
319
+ | Key | Action |
320
+ | ---------------------------------------------------------- | ----------------------------------------------------------- |
321
+ | <kbd>Enter</kbd> / <kbd>Space</kbd> / <kbd>ArrowDown</kbd> | Opens the menu and focuses its first item |
322
+ | <kbd>ArrowUp</kbd> | Opens the menu and focuses its last item |
323
+ | <kbd>Escape</kbd> | In the open menu: closes it and returns focus to the button |
324
+ | <kbd>Tab</kbd> | In the open menu: closes it; focus moves on past the button |
325
+
326
+ ## Inputs
327
+
328
+ | Name | Type | Default | Description |
329
+ | ----------- | ---------------------------------------------------------------------------------- | --------------- | -------------------------------------------------------------------------------------------------------------------------------- |
330
+ | `menu` | `TemplateRef<unknown>` | required | The `ng-template` holding the `bf-menu` to open. |
331
+ | `variant` | `BfMenuButtonVariant` (`'outline' \| 'text' \| 'tonal'`) | `'outline'` | Visual style: outline (default), text, tonal. |
332
+ | `size` | `BfSize \| undefined` (`'sm' \| 'md' \| 'lg' \| undefined`) | `undefined` | Height step: sm 32, md 40, lg 48 px; unset follows `provideBfDefaults()` (`controlSize`, `md` by default). |
333
+ | `placement` | `BfMenuPlacement` (`'below-start' \| 'below-end' \| 'above-start' \| 'above-end'`) | `'below-start'` | Where the menu opens against the button; it flips when it would overflow. |
334
+ | `label` | `string` | `''` | Accessible name (`aria-label`). Required with `iconOnly`; with a visible label leave it empty or start it with the visible text. |
335
+ | `iconOnly` | `boolean` | `false` | Square button holding only the projected icon, like `bf-icon-button`; set `label`. |
336
+ | `disabled` | `boolean` | `false` | Disables the button; the menu cannot open. |
337
+
338
+ ## Outputs
339
+
340
+ | Name | Payload | Description |
341
+ | -------- | ------- | ---------------- |
342
+ | `opened` | `void` | The menu opened. |
343
+ | `closed` | `void` | The menu closed. |
344
+
345
+ ## Slots
346
+
347
+ - `icon-start`: An icon before the label.
348
+ - `default`: The label (the accessible name of an icon-only button).
349
+
350
+ ## Notes
351
+
352
+ ### Do
353
+
354
+ - Name the button after what the menu holds ("Export", "Sort", "Actions").
355
+ - Use the icon-only variant with a `label` for row actions ("More actions").
356
+
357
+ ### Don't
358
+
359
+ - Don't use a menu button for a single action; use a `bf-button`.
360
+ - Don't use it to pick a form value; use `bf-select` or `bf-radio-group`.
361
+
362
+ ## Migration notes
363
+
364
+ ### 0.3.0
365
+
366
+ New `bf-menu-button` in `@bitforgehq/ui/overlay`: a native `<button>` that opens a `bf-menu`
367
+ (`menu`, a `TemplateRef`) with `aria-haspopup="menu"` and `aria-expanded`, styled like
368
+ `bf-button` (`variant` `outline` (default), `text`, `tonal`; a CSS caret) or, with `iconOnly`,
369
+ like `bf-icon-button`. `size` (unset follows `controlSize`), `placement`, `label` (the
370
+ `aria-label`, required with `iconOnly`), `disabled`, and `opened`/`closed` outputs. Its classes
371
+ come from `bf-button`'s and `bf-icon-button`'s own class maps (internal `ɵ` exports of
372
+ `@bitforgehq/ui`), so the looks never drift.
373
+
374
+ JSDoc of `menu-button.component.ts` for the API reference: `@summary`/`@since` on the exported
375
+ symbols; `@slot` docs for `icon-start`, `default`. No behaviour change.
376
+
377
+ **Behaviour:** a pressed state (F-045): the button and icon-only forms take `bf-button`'s and `bf-icon-button`'s pressed fills from their shared class maps. None while disabled; under forced colours the system colours win.