@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,420 @@
1
+ # bf-desktop-toast
2
+
3
+ Desktop notification card in a bottom corner stack of up to three
4
+
5
+ `BfDesktopToastComponent` from `@bitforgehq/ui/overlay` · Overlays · since 0.3.0
6
+
7
+ ## Example
8
+
9
+ ```ts
10
+ import { Component, inject, signal } from '@angular/core';
11
+ import { BfButtonComponent } from '@bitforgehq/ui';
12
+ import { BfDesktopToastService, type BfToastRef } from '@bitforgehq/ui/overlay';
13
+
14
+ /**
15
+ * The bf-desktop-toast overview.
16
+ *
17
+ * Live demo of `BfDesktopToastService`: "Show toast" shows a timed toast, "Delete with undo" a
18
+ * persistent one with an Undo action, "Show three" fills the stack. The toasts appear in the
19
+ * page's bottom-right corner; the status line reports how the Undo toast ended. The triggers and
20
+ * the status line carry `data-testid`s for the end-to-end suite.
21
+ */
22
+ @Component({
23
+ selector: 'app-example-desktop-toast-overview',
24
+ imports: [BfButtonComponent],
25
+ template: `
26
+ <div class="flex flex-col gap-4">
27
+ <div class="flex flex-wrap gap-2">
28
+ <bf-button data-testid="toast-show" (clicked)="showOne()">Show toast</bf-button>
29
+ <bf-button data-testid="toast-undo" variant="outline" (clicked)="deleteWithUndo()"
30
+ >Delete with undo</bf-button
31
+ >
32
+ <bf-button data-testid="toast-three" variant="tonal" (clicked)="showThree()"
33
+ >Show three</bf-button
34
+ >
35
+ </div>
36
+ <p data-status data-testid="toast-result" class="text-caption text-text-muted">
37
+ {{ result() }}
38
+ </p>
39
+ </div>
40
+ `,
41
+ })
42
+ export class DesktopToastOverviewExampleComponent {
43
+ private readonly toasts = inject(BfDesktopToastService);
44
+ private undo: BfToastRef | null = null;
45
+
46
+ readonly result = signal('');
47
+
48
+ showOne(): void {
49
+ this.toasts.show({ message: 'Changes saved' });
50
+ }
51
+
52
+ async deleteWithUndo(): Promise<void> {
53
+ this.undo?.dismiss();
54
+ const ref = this.toasts.show({
55
+ message: 'Invoice INV-204 deleted',
56
+ action: { label: 'Undo', handler: () => this.result.set('Restored INV-204.') },
57
+ });
58
+ this.undo = ref;
59
+ const reason = await ref.afterDismissed;
60
+ if (this.undo === ref) {
61
+ this.undo = null;
62
+ }
63
+ if (reason !== 'action') {
64
+ this.result.set('INV-204 stays deleted (not really, this is a demo).');
65
+ }
66
+ }
67
+
68
+ showThree(): void {
69
+ this.toasts.show({ message: 'Export started' });
70
+ this.toasts.show({ message: '3 files uploaded' });
71
+ this.toasts.show({ message: 'Report shared with the team' });
72
+ }
73
+ }
74
+ ```
75
+
76
+ ## Overview
77
+
78
+ The card `BfDesktopToastService.show()` renders in its notification stack: the message and an
79
+ optional action button on a surface card. It is not a live region (the service announces each
80
+ toast once through CDK `LiveAnnouncer`) and has no close button; `Escape` with focus inside it
81
+ emits `escaped`, which dismisses the toast. Apps do not place it in templates.
82
+
83
+ Desktop notifications for web apps, from `@bitforgehq/ui/overlay` (needs `@angular/cdk`, no
84
+ Capacitor). `BfDesktopToastService.show(options)` adds a `bf-desktop-toast` card to a stack in
85
+ the bottom-right corner of the viewport (bottom left in right-to-left pages) and returns a
86
+ `BfToastRef`. Up to three toasts are visible at once; later ones wait and show as earlier ones
87
+ end. You never place `<bf-desktop-toast>` in a template yourself. For Capacitor apps use the
88
+ `/mobile` `BfToastService`, which follows the same rules one toast at a time.
89
+
90
+ ## When to use
91
+
92
+ Use `BfDesktopToastService` in a desktop web app for a short status after an action, with at most one follow-up: "Saved", "Invoice deleted. Undo". Use [bf-alert](https://bitforge-ui.web.app/components/alert) instead for a message that must stay on the page, [BfDialogService](https://bitforge-ui.web.app/api/bf-dialog-service) `confirm()` when the user must answer, and the `/mobile` [BfToastService](https://bitforge-ui.web.app/api/bf-toast-service) in a Capacitor app.
93
+
94
+ ## Basic usage
95
+
96
+ ```ts
97
+ import { Component, inject, signal } from '@angular/core';
98
+ import { BfButtonComponent } from '@bitforgehq/ui';
99
+ import { BfDesktopToastService } from '@bitforgehq/ui/overlay';
100
+
101
+ /**
102
+ * A toast and how it ended.
103
+ *
104
+ * `afterDismissed` resolves with the reason: `timeout`, `action` or `dismiss`.
105
+ */
106
+ @Component({
107
+ selector: 'app-example-desktop-toast-basic',
108
+ imports: [BfButtonComponent],
109
+ template: `
110
+ <div class="flex flex-col items-start gap-3">
111
+ <bf-button (clicked)="save()">Save</bf-button>
112
+ <p aria-live="polite" class="text-caption text-text-muted">{{ result() }}</p>
113
+ </div>
114
+ `,
115
+ })
116
+ export class DesktopToastBasicExampleComponent {
117
+ private readonly toasts = inject(BfDesktopToastService);
118
+ protected readonly result = signal('');
119
+
120
+ protected async save(): Promise<void> {
121
+ const ref = this.toasts.show({ message: 'Changes saved' });
122
+ const reason = await ref.afterDismissed; // 'timeout' | 'action' | 'dismiss'
123
+ this.result.set(`The toast ended: ${reason}.`);
124
+ }
125
+ }
126
+ ```
127
+
128
+ `dismiss()` ends the toast right away: it closes a visible toast, or drops a waiting one before
129
+ it ever shows.
130
+
131
+ ```ts
132
+ import { Component, inject } from '@angular/core';
133
+ import { BfButtonComponent } from '@bitforgehq/ui';
134
+ import { BfDesktopToastService, type BfToastRef } from '@bitforgehq/ui/overlay';
135
+
136
+ /**
137
+ * A toast that stays until it is dismissed.
138
+ *
139
+ * `duration: 0` keeps "Syncing…" up; `dismiss()` ends it when the work is done.
140
+ */
141
+ @Component({
142
+ selector: 'app-example-desktop-toast-persistent',
143
+ imports: [BfButtonComponent],
144
+ template: `
145
+ <div class="flex flex-wrap gap-2">
146
+ <bf-button variant="outline" (clicked)="start()">Start sync</bf-button>
147
+ <bf-button variant="text" (clicked)="finish()">Finish sync</bf-button>
148
+ </div>
149
+ `,
150
+ })
151
+ export class DesktopToastPersistentExampleComponent {
152
+ private readonly toasts = inject(BfDesktopToastService);
153
+ private ref: BfToastRef | null = null;
154
+
155
+ protected start(): void {
156
+ this.ref?.dismiss();
157
+ this.ref = this.toasts.show({ message: 'Syncing…', duration: 0 });
158
+ }
159
+
160
+ protected finish(): void {
161
+ this.ref?.dismiss();
162
+ this.ref = null;
163
+ }
164
+ }
165
+ ```
166
+
167
+ ## Action
168
+
169
+ `action` renders a real `<button>` in the card. Its `handler` runs before the toast closes with
170
+ reason `'action'`. A toast with an action stays until the action, `Escape` or `dismiss()` ends it
171
+ (see Timing), so keyboard, switch and screen-reader users can reach it.
172
+
173
+ ```ts
174
+ import { Component, inject, signal } from '@angular/core';
175
+ import { BfButtonComponent } from '@bitforgehq/ui';
176
+ import { BfDesktopToastService } from '@bitforgehq/ui/overlay';
177
+
178
+ /** A toast with an Undo action; it stays until Undo, Escape or a dismissal ends it. */
179
+ @Component({
180
+ selector: 'app-example-desktop-toast-action',
181
+ imports: [BfButtonComponent],
182
+ template: `
183
+ <div class="flex flex-col items-start gap-3">
184
+ <bf-button variant="danger" (clicked)="remove()">Delete INV-204</bf-button>
185
+ <p aria-live="polite" class="text-caption text-text-muted">{{ result() }}</p>
186
+ </div>
187
+ `,
188
+ })
189
+ export class DesktopToastActionExampleComponent {
190
+ private readonly toasts = inject(BfDesktopToastService);
191
+ protected readonly result = signal('');
192
+
193
+ protected remove(): void {
194
+ this.result.set('INV-204 deleted.');
195
+ this.toasts.show({
196
+ message: 'Invoice INV-204 deleted',
197
+ action: { label: 'Undo', handler: () => this.result.set('INV-204 restored.') },
198
+ });
199
+ }
200
+ }
201
+ ```
202
+
203
+ ## Timing
204
+
205
+ `duration` (ms) is how long a toast stays before it ends with `'timeout'`. The default is `3000`
206
+ for a toast without an action and `0` (persistent) for one with an action: an "Undo" that
207
+ vanishes after 3 seconds is out of reach for many keyboard and screen-reader users
208
+ ([WCAG 2.2.1 Timing Adjustable](https://www.w3.org/WAI/WCAG22/Understanding/timing-adjustable)).
209
+ An explicit `duration` always wins.
210
+
211
+ While the pointer is over the stack or focus is inside it, **every** visible toast pauses, and
212
+ each resumes with the time it had left once both have left. The stack never reflows under the
213
+ reader's pointer. The oldest toast leaves first, because its timer started first.
214
+
215
+ ## Stacking and replacing
216
+
217
+ Up to `maxVisible` toasts (default 3) are visible at once, the newest nearest the anchored edge
218
+ (at the bottom of a bottom stack, at the top of a top stack). A `show()` while the stack is full
219
+ waits in order and shows once a visible toast ends.
220
+
221
+ A persistent toast (`duration: 0`, the default with an action) never blocks the queue: the next
222
+ `show()` dismisses every persistent toast, visible or waiting, and each resolves `'dismiss'`. A
223
+ superseded "Undo" is stale anyway. A navigation never closes a toast (a "Saved" survives the
224
+ navigation that follows the save), so keep the ref of an action toast and `dismiss()` it once it
225
+ is stale, for example when the user navigates away.
226
+
227
+ ```ts
228
+ import { Component, inject, signal } from '@angular/core';
229
+ import { BfButtonComponent } from '@bitforgehq/ui';
230
+ import { BfDesktopToastService, type BfToastRef } from '@bitforgehq/ui/overlay';
231
+
232
+ /**
233
+ * One Undo toast at a time.
234
+ *
235
+ * Each archive dismisses the previous Undo toast before it shows its own.
236
+ */
237
+ @Component({
238
+ selector: 'app-example-desktop-toast-replace',
239
+ imports: [BfButtonComponent],
240
+ template: `
241
+ <div class="flex flex-col items-start gap-3">
242
+ <bf-button variant="outline" (clicked)="archive()">Archive the next task</bf-button>
243
+ <p class="text-caption text-text-muted">{{ archived() }} archived</p>
244
+ </div>
245
+ `,
246
+ })
247
+ export class DesktopToastReplaceExampleComponent {
248
+ private readonly toasts = inject(BfDesktopToastService);
249
+ private undo: BfToastRef | null = null;
250
+ protected readonly archived = signal(0);
251
+
252
+ protected archive(): void {
253
+ this.archived.update((n) => n + 1);
254
+ this.undo?.dismiss();
255
+ this.undo = this.toasts.show({
256
+ message: 'Archived',
257
+ action: { label: 'Undo', handler: () => this.restore() },
258
+ });
259
+ }
260
+
261
+ private restore(): void {
262
+ this.archived.update((n) => n - 1);
263
+ }
264
+ }
265
+ ```
266
+
267
+ ## Position and configuration
268
+
269
+ `provideBfDesktopToast()` in `ApplicationConfig.providers` sets `position`
270
+ (`'bottom-end'` (default), `'bottom-center'`, `'top-end'` or `'top-center'`), `maxVisible`
271
+ (default 3) and `ariaLabel` (the region's name; empty uses the `notifications` label). The stack
272
+ sits 24 px in from the viewport edges plus the safe-area inset (the end edge is the left one in a
273
+ right-to-left page, with the left safe area). Cards are 24 rem wide, capped at
274
+ the viewport width minus a 16 px gutter on each side.
275
+
276
+ ```ts
277
+ import { type ApplicationConfig, Component, inject } from '@angular/core';
278
+ import { BfButtonComponent } from '@bitforgehq/ui';
279
+ import { BfDesktopToastService, provideBfDesktopToast } from '@bitforgehq/ui/overlay';
280
+
281
+ /** The app's providers: toasts at the top centre, at most two at once. */
282
+ export const appConfig: ApplicationConfig = {
283
+ providers: [provideBfDesktopToast({ position: 'top-center', maxVisible: 2 })],
284
+ };
285
+
286
+ /**
287
+ * The toast position and stack size, set once for the app.
288
+ *
289
+ * `provideBfDesktopToast()` goes in the application config; every `show()` then uses it.
290
+ *
291
+ * @preview none
292
+ */
293
+ @Component({
294
+ selector: 'app-example-desktop-toast-config',
295
+ imports: [BfButtonComponent],
296
+ template: `<bf-button (clicked)="notify()">Notify</bf-button>`,
297
+ })
298
+ export class DesktopToastConfigExampleComponent {
299
+ private readonly toasts = inject(BfDesktopToastService);
300
+
301
+ protected notify(): void {
302
+ this.toasts.show({ message: 'Shown at the top centre' });
303
+ }
304
+ }
305
+ ```
306
+
307
+ The region's name comes from `provideBfLabels({ notifications })`. The service is a root
308
+ singleton: pass the feature's injector with the toast, `show({ message, injector:
309
+ inject(Injector) })`, so a feature-level `provideBfLabels()` names the region that toast opens.
310
+
311
+ ## Desktop
312
+
313
+ Built for desktop: the stack sits in the bottom-end corner, clear of the page's own controls. While the pointer is over the stack every visible toast pauses, and the stack never reflows under it. The action underlines on hover and shows the focus ring on keyboard focus only. Tell keyboard users about <kbd>F6</kbd>, since the stack comes after the page in the tab order.
314
+
315
+ ## Theming
316
+
317
+ - Card: `bg-surface text-text`, a 1 px `border-border`, `rounded-card`, `shadow-floating`,
318
+ `text-body`.
319
+ - Action: `text-primary-strong`, bold, underlined on hover, with the focus ring (`ring-ring`).
320
+ - Width: `min(24rem, 100vw - 2 × --spacing-gutter)`.
321
+
322
+ ## Accessibility
323
+
324
+ - Announcement: each toast is announced exactly once, politely, through the CDK `LiveAnnouncer`
325
+ as it shows: the message, then the action label ("Invoice INV-204 deleted. Undo"). A waiting
326
+ toast is announced when it shows. Neither the card nor the stack is a live region (no
327
+ `aria-live`, no `role="status"`), so nothing is read twice
328
+ ([WCAG 4.1.3 Status Messages](https://www.w3.org/WAI/WCAG22/Understanding/status-messages)).
329
+ - Landmark: the stack is a `region` named "Notifications" (the `notifications` label or
330
+ `ariaLabel`). Screen-reader users can jump to it from the landmarks list. It exists only while a
331
+ toast is visible.
332
+ - Focus: showing a toast never moves focus. There is no backdrop, no focus trap, no scroll lock,
333
+ and nothing becomes `inert`. The stack is rendered at the end of the page, so Tab reaches the
334
+ action button only after the page content, which is impractical on a long page.
335
+ - `F6` shortcut: while a toast with an action is visible, `F6` (without Ctrl, Alt or Meta) moves
336
+ focus to the action of the newest such toast, and `F6` again inside the stack moves focus back
337
+ to where it was (landmark-region navigation, as in the
338
+ [APG landmark regions practice](https://www.w3.org/WAI/ARIA/apg/practices/landmark-regions/)).
339
+ Without an action toast the key is left to the browser. `F6` does nothing while a modal overlay
340
+ is open (`BfDialogService`, the `/mobile` sheet, modal or alert): its focus trap is APG-modal, so
341
+ the toast stays reachable only by pointer until the modal closes. Browsers also use `F6` to move
342
+ between the address bar and the page; where a browser keeps the key for itself, only Tab reaches
343
+ the action. Tell keyboard users about the shortcut (for example in your app's keyboard help), and
344
+ offer the same action somewhere persistent.
345
+ - Keyboard: `Escape` while focus is inside a toast dismisses it (`'dismiss'`). The key is claimed
346
+ there, so a dialog underneath does not also close. There is no close button: a timed toast
347
+ times out, and a persistent one ends with its action, `Escape`, `dismiss()` or the next
348
+ `show()`.
349
+ - Focus return: when a toast that holds focus closes, focus returns to the element focused last
350
+ outside it, if that element is still in the page.
351
+ - Timing: pointer over the stack or focus inside it pauses every visible toast (see Timing).
352
+ - Motion: a card fades in while sliding 8 px in from the anchored edge and fades out. Under
353
+ `prefers-reduced-motion` it only fades.
354
+
355
+ ### Keyboard
356
+
357
+ | Key | Action |
358
+ | ----------------------------------- | -------------------------------------------------------------------------------------------------------------------------------- |
359
+ | <kbd>F6</kbd> | From the page: moves focus to the action of the newest toast that has one; inside the stack: moves it back to where it came from |
360
+ | <kbd>Tab</kbd> | Reaches the toast actions after the page content |
361
+ | <kbd>Enter</kbd> / <kbd>Space</kbd> | Runs the focused action; the toast closes with `'action'` |
362
+ | <kbd>Escape</kbd> | With focus inside a toast: dismisses it (`'dismiss'`); a dialog underneath stays open |
363
+
364
+ ## Inputs
365
+
366
+ | Name | Type | Default | Description |
367
+ | ------------- | -------- | -------- | --------------------------------------------------- |
368
+ | `message` | `string` | required | Text of the toast; long words wrap. |
369
+ | `actionLabel` | `string` | `''` | Text of the action button; empty renders no button. |
370
+
371
+ ## Outputs
372
+
373
+ | Name | Payload | Description |
374
+ | --------------- | ------- | ---------------------------------------------------------------------- |
375
+ | `actionClicked` | `void` | The action button was clicked. |
376
+ | `escaped` | `void` | `Escape` was pressed with focus inside the toast (the key is claimed). |
377
+
378
+ ## Notes
379
+
380
+ ### Do
381
+
382
+ - Keep the message short, one sentence, and make the action a single verb ("Undo", "Retry",
383
+ "View").
384
+ - Keep the ref of a persistent toast and `dismiss()` it once it is stale.
385
+ - Offer anything you put in a toast's action somewhere persistent as well.
386
+
387
+ ### Don't
388
+
389
+ - Don't use a toast for something the user must see or answer before continuing. Use a dialog
390
+ (`BfDialogService.confirm()`) instead.
391
+ - Don't show a toast for every background event. Three are visible at most, and the rest wait.
392
+ - Don't set a short `duration` on a toast with an action.
393
+
394
+ ## Migration notes
395
+
396
+ ### 0.3.0
397
+
398
+ The card's action button output is `actionClicked` (an action emits `clicked`, a sub-part's
399
+ action `<part>Clicked`); `BfDesktopToastService` binds it.
400
+
401
+ New `BfDesktopToastService` and `bf-desktop-toast` in `@bitforgehq/ui/overlay`: toasts for desktop
402
+ and web apps with no Capacitor dependency. `show({ message, action?, duration?, injector? })`
403
+ returns a `BfToastRef` and follows the `/mobile` toast's rules: 3000 ms by default, persistent
404
+ (`duration` 0) with an action, a later `show()` replaces every persistent toast (`'dismiss'`),
405
+ `Escape` inside a toast dismisses it, focus returns only when it was inside the closing toast, one
406
+ polite `LiveAnnouncer` announcement per toast, everything ends with the injector. Up to three
407
+ cards are visible at once in one passive overlay pane, bottom right (bottom left in RTL) 24 px in
408
+ plus the safe area, the newest nearest the edge; later toasts wait. The pointer over the stack or
409
+ focus inside it pauses every visible toast. The stack is a `region` named by the new
410
+ `notifications` label ("Notifications"); no backdrop, no focus move, no scroll lock, nothing
411
+ `inert`. Cards fade in with an 8 px slide (a fade under reduced motion). A navigation never closes
412
+ a toast. `F6` from the page moves focus to the action of the newest toast that has one, and `F6`
413
+ inside the stack moves it back (the key is left to the browser otherwise). In a right-to-left page
414
+ the end inset reads the left safe area (`--spacing-safe-left`).
415
+ `provideBfDesktopToast(config)` sets `position` (`'bottom-end'` (default), `'bottom-center'`,
416
+ `'top-end'` or `'top-center'`), `maxVisible` (default 3) and `ariaLabel` (the region name; empty
417
+ uses the label), over `BF_DESKTOP_TOAST_DEFAULTS`.
418
+
419
+ JSDoc of `desktop-toast.service.ts` for the API reference: `@summary`/`@since` on the exported
420
+ symbols; `@default` on the option properties the code applies; `@param` docs. No behaviour change.