@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.
- package/CHANGELOG.md +3665 -0
- package/LICENSE +21 -0
- package/README.md +175 -0
- package/ai/MANIFEST.index.json +383 -0
- package/ai/MANIFEST.json +7528 -0
- package/ai/REFERENCE.index.json +1461 -0
- package/ai/REFERENCE.json +7213 -0
- package/ai/UI_GUIDE.md +1159 -0
- package/ai/components/action-bar.md +209 -0
- package/ai/components/action-sheet.md +526 -0
- package/ai/components/alert-dialog.md +509 -0
- package/ai/components/alert.md +376 -0
- package/ai/components/avatar.md +146 -0
- package/ai/components/back-button.md +286 -0
- package/ai/components/badge.md +171 -0
- package/ai/components/button.md +431 -0
- package/ai/components/card.md +303 -0
- package/ai/components/checkbox.md +418 -0
- package/ai/components/chip-group.md +205 -0
- package/ai/components/chip.md +300 -0
- package/ai/components/content.md +474 -0
- package/ai/components/desktop-toast.md +420 -0
- package/ai/components/dialog.md +962 -0
- package/ai/components/empty-state.md +146 -0
- package/ai/components/field.md +319 -0
- package/ai/components/header.md +809 -0
- package/ai/components/icon-button.md +221 -0
- package/ai/components/icon.md +176 -0
- package/ai/components/input.md +455 -0
- package/ai/components/list-item.md +326 -0
- package/ai/components/list.md +178 -0
- package/ai/components/menu-button.md +377 -0
- package/ai/components/menu.md +661 -0
- package/ai/components/modal.md +613 -0
- package/ai/components/number-input.md +423 -0
- package/ai/components/page.md +299 -0
- package/ai/components/progress.md +215 -0
- package/ai/components/radio-group.md +432 -0
- package/ai/components/refresher.md +377 -0
- package/ai/components/segmented.md +517 -0
- package/ai/components/select.md +510 -0
- package/ai/components/sheet.md +845 -0
- package/ai/components/spinner.md +171 -0
- package/ai/components/stack-outlet.md +661 -0
- package/ai/components/tab-bar.md +427 -0
- package/ai/components/tab.md +295 -0
- package/ai/components/tabs.md +408 -0
- package/ai/components/textarea.md +371 -0
- package/ai/components/toast.md +522 -0
- package/ai/components/toggle.md +387 -0
- package/ai/reference/bf-action-directive.md +21 -0
- package/ai/reference/bf-action-sheet-action-role.md +27 -0
- package/ai/reference/bf-action-sheet-action.md +28 -0
- package/ai/reference/bf-action-sheet-options.md +28 -0
- package/ai/reference/bf-action-sheet-service.md +33 -0
- package/ai/reference/bf-alert-dialog-button-role.md +27 -0
- package/ai/reference/bf-alert-dialog-button.md +27 -0
- package/ai/reference/bf-alert-dialog-options.md +29 -0
- package/ai/reference/bf-alert-dialog-service.md +34 -0
- package/ai/reference/bf-alert-variant.md +28 -0
- package/ai/reference/bf-autocapitalize.md +30 -0
- package/ai/reference/bf-avatar-size.md +27 -0
- package/ai/reference/bf-avatar-variant.md +28 -0
- package/ai/reference/bf-back-at-root.md +28 -0
- package/ai/reference/bf-back-button-service.md +40 -0
- package/ai/reference/bf-badge-variant.md +29 -0
- package/ai/reference/bf-button-size.md +29 -0
- package/ai/reference/bf-button-type.md +28 -0
- package/ai/reference/bf-button-variant.md +29 -0
- package/ai/reference/bf-card-padding.md +26 -0
- package/ai/reference/bf-card-variant.md +28 -0
- package/ai/reference/bf-chip-group-mode.md +27 -0
- package/ai/reference/bf-chip-variant.md +26 -0
- package/ai/reference/bf-compare-with.md +24 -0
- package/ai/reference/bf-component-overlay-ref.md +33 -0
- package/ai/reference/bf-confirm-button-role.md +26 -0
- package/ai/reference/bf-confirm-button.md +27 -0
- package/ai/reference/bf-confirm-options.md +31 -0
- package/ai/reference/bf-confirm-size.md +26 -0
- package/ai/reference/bf-content-scroll-event.md +21 -0
- package/ai/reference/bf-defaults-options.md +32 -0
- package/ai/reference/bf-defaults.md +28 -0
- package/ai/reference/bf-desktop-toast-config.md +28 -0
- package/ai/reference/bf-desktop-toast-harness.md +33 -0
- package/ai/reference/bf-desktop-toast-options.md +28 -0
- package/ai/reference/bf-desktop-toast-position.md +28 -0
- package/ai/reference/bf-desktop-toast-service.md +42 -0
- package/ai/reference/bf-dialog-config.md +44 -0
- package/ai/reference/bf-dialog-harness.md +52 -0
- package/ai/reference/bf-dialog-ref.md +44 -0
- package/ai/reference/bf-dialog-role.md +26 -0
- package/ai/reference/bf-dialog-service.md +55 -0
- package/ai/reference/bf-dialog-size.md +31 -0
- package/ai/reference/bf-dialog-template-context.md +32 -0
- package/ai/reference/bf-empty-state-heading-level.md +26 -0
- package/ai/reference/bf-enter-key-hint.md +31 -0
- package/ai/reference/bf-field-control-class.md +32 -0
- package/ai/reference/bf-field-control-state.md +28 -0
- package/ai/reference/bf-field-size.md +29 -0
- package/ai/reference/bf-haptics-impact-style.md +27 -0
- package/ai/reference/bf-haptics-notification-type.md +27 -0
- package/ai/reference/bf-haptics-service.md +41 -0
- package/ai/reference/bf-harness-filters.md +29 -0
- package/ai/reference/bf-horizontal-scroll-directive.md +129 -0
- package/ai/reference/bf-icon-button-variant.md +28 -0
- package/ai/reference/bf-icon-size.md +27 -0
- package/ai/reference/bf-input-autocapitalize.md +36 -0
- package/ai/reference/bf-input-enter-key-hint.md +38 -0
- package/ai/reference/bf-input-input-mode.md +38 -0
- package/ai/reference/bf-input-mode.md +32 -0
- package/ai/reference/bf-input-type.md +35 -0
- package/ai/reference/bf-jsdom-shim-options.md +22 -0
- package/ai/reference/bf-keyboard-assist-behavior.md +26 -0
- package/ai/reference/bf-keyboard-assist-directive.md +185 -0
- package/ai/reference/bf-keyboard-phase.md +30 -0
- package/ai/reference/bf-keyboard-service.md +50 -0
- package/ai/reference/bf-labels-overrides.md +26 -0
- package/ai/reference/bf-labels.md +44 -0
- package/ai/reference/bf-menu-button-harness.md +40 -0
- package/ai/reference/bf-menu-button-variant.md +27 -0
- package/ai/reference/bf-menu-divider-directive.md +13 -0
- package/ai/reference/bf-menu-group-directive.md +14 -0
- package/ai/reference/bf-menu-harness.md +55 -0
- package/ai/reference/bf-menu-item-checkbox-directive.md +31 -0
- package/ai/reference/bf-menu-item-directive.md +31 -0
- package/ai/reference/bf-menu-item-harness.md +52 -0
- package/ai/reference/bf-menu-item-radio-directive.md +29 -0
- package/ai/reference/bf-menu-placement.md +29 -0
- package/ai/reference/bf-menu-trigger-directive.md +46 -0
- package/ai/reference/bf-modal-options.md +30 -0
- package/ai/reference/bf-modal-presentation.md +26 -0
- package/ai/reference/bf-modal-service.md +38 -0
- package/ai/reference/bf-nav-controller.md +59 -0
- package/ai/reference/bf-nav-direction.md +23 -0
- package/ai/reference/bf-nav-target.md +21 -0
- package/ai/reference/bf-number-input-mode.md +28 -0
- package/ai/reference/bf-number-input-step.md +25 -0
- package/ai/reference/bf-overlay-auto-focus.md +29 -0
- package/ai/reference/bf-overlay-controller.md +22 -0
- package/ai/reference/bf-overlay-host.md +36 -0
- package/ai/reference/bf-overlay-options.md +53 -0
- package/ai/reference/bf-overlay-position.md +30 -0
- package/ai/reference/bf-overlay-ref.md +53 -0
- package/ai/reference/bf-overlay-role.md +27 -0
- package/ai/reference/bf-overlay-service.md +53 -0
- package/ai/reference/bf-overlay-stack-options.md +24 -0
- package/ai/reference/bf-overlay-stack.md +81 -0
- package/ai/reference/bf-overlay-template-context.md +24 -0
- package/ai/reference/bf-overscroll-stretch-directive.md +163 -0
- package/ai/reference/bf-page-context.md +24 -0
- package/ai/reference/bf-platform-service.md +53 -0
- package/ai/reference/bf-platform.md +28 -0
- package/ai/reference/bf-progress-size.md +26 -0
- package/ai/reference/bf-progress-tone.md +28 -0
- package/ai/reference/bf-radio-group-harness.md +49 -0
- package/ai/reference/bf-radio-group-orientation.md +26 -0
- package/ai/reference/bf-radio-option.md +30 -0
- package/ai/reference/bf-refresher-refresh-event.md +22 -0
- package/ai/reference/bf-segment-option.md +29 -0
- package/ai/reference/bf-segmented-harness.md +48 -0
- package/ai/reference/bf-segmented-size.md +29 -0
- package/ai/reference/bf-select-harness.md +70 -0
- package/ai/reference/bf-select-option.md +29 -0
- package/ai/reference/bf-sheet-auto-focus.md +29 -0
- package/ai/reference/bf-sheet-options.md +32 -0
- package/ai/reference/bf-sheet-ref.md +45 -0
- package/ai/reference/bf-sheet-service.md +34 -0
- package/ai/reference/bf-shell-config-mode.md +28 -0
- package/ai/reference/bf-shell-config.md +38 -0
- package/ai/reference/bf-shell-dynamic-type-limit.md +21 -0
- package/ai/reference/bf-shell-dynamic-type.md +25 -0
- package/ai/reference/bf-shell-mode.md +28 -0
- package/ai/reference/bf-shell-testing-options.md +26 -0
- package/ai/reference/bf-size.md +45 -0
- package/ai/reference/bf-spinner-size.md +27 -0
- package/ai/reference/bf-stack-change.md +22 -0
- package/ai/reference/bf-stack-reuse-strategy.md +32 -0
- package/ai/reference/bf-stack-transition-event.md +26 -0
- package/ai/reference/bf-stack-transition.md +26 -0
- package/ai/reference/bf-stacked-overlay.md +23 -0
- package/ai/reference/bf-system-bars-style.md +38 -0
- package/ai/reference/bf-tab-bar-controller.md +51 -0
- package/ai/reference/bf-tab-bar-host.md +52 -0
- package/ai/reference/bf-tab-context.md +32 -0
- package/ai/reference/bf-tab-links.md +37 -0
- package/ai/reference/bf-testing-options.md +27 -0
- package/ai/reference/bf-text-scale-service.md +35 -0
- package/ai/reference/bf-theme.md +27 -0
- package/ai/reference/bf-toast-action.md +25 -0
- package/ai/reference/bf-toast-controller.md +17 -0
- package/ai/reference/bf-toast-dismiss-reason.md +29 -0
- package/ai/reference/bf-toast-options.md +29 -0
- package/ai/reference/bf-toast-position.md +26 -0
- package/ai/reference/bf-toast-ref.md +41 -0
- package/ai/reference/bf-toast-service.md +39 -0
- package/ai/reference/bf-toggle-label-position.md +26 -0
- package/ai/reference/bf-transition-kind.md +28 -0
- package/ai/reference/bf-validation-message.md +31 -0
- package/ai/reference/bf-validation-messages.md +33 -0
- package/ai/reference/bf-view-event.md +28 -0
- package/ai/reference/bf-view-lifecycle.md +41 -0
- package/ai/reference/bf-view-state.md +28 -0
- package/ai/reference/bf_default_labels.md +21 -0
- package/ai/reference/bf_default_values.md +21 -0
- package/ai/reference/bf_defaults.md +21 -0
- package/ai/reference/bf_desktop_toast_defaults.md +21 -0
- package/ai/reference/bf_dialog_data.md +17 -0
- package/ai/reference/bf_labels.md +21 -0
- package/ai/reference/bf_overlay_data.md +19 -0
- package/ai/reference/bf_overlay_host.md +21 -0
- package/ai/reference/bf_sheet_default_snap_points.md +17 -0
- package/ai/reference/bf_shell_config.md +21 -0
- package/ai/reference/bf_shell_defaults.md +21 -0
- package/ai/reference/bf_tab_bar.md +21 -0
- package/ai/reference/bf_tab_bar_controller.md +21 -0
- package/ai/reference/bf_tab_context.md +21 -0
- package/ai/reference/bf_tab_links.md +21 -0
- package/ai/reference/bf_theme_storage.md +19 -0
- package/ai/reference/create-transition.md +36 -0
- package/ai/reference/duration_fast.md +17 -0
- package/ai/reference/duration_md.md +17 -0
- package/ai/reference/duration_overlay.md +17 -0
- package/ai/reference/duration_push.md +17 -0
- package/ai/reference/fade-down.md +26 -0
- package/ai/reference/fade-in.md +25 -0
- package/ai/reference/fade-out.md +25 -0
- package/ai/reference/fade-up.md +26 -0
- package/ai/reference/inject-shell-mode.md +23 -0
- package/ai/reference/install-bf-jsdom-shims.md +31 -0
- package/ai/reference/ios_duration.md +17 -0
- package/ai/reference/ios_easing.md +17 -0
- package/ai/reference/is-text-entry.md +25 -0
- package/ai/reference/max_complete_ms.md +17 -0
- package/ai/reference/md-transition.md +31 -0
- package/ai/reference/md_duration.md +17 -0
- package/ai/reference/md_easing.md +17 -0
- package/ai/reference/overlay_duration.md +21 -0
- package/ai/reference/overlay_easing.md +19 -0
- package/ai/reference/overlay_reduced_duration.md +17 -0
- package/ai/reference/pop-frames-at.md +25 -0
- package/ai/reference/prefers-reduced-motion.md +25 -0
- package/ai/reference/provide-bf-defaults.md +33 -0
- package/ai/reference/provide-bf-desktop-toast.md +28 -0
- package/ai/reference/provide-bf-labels.md +38 -0
- package/ai/reference/provide-bf-shell-testing.md +34 -0
- package/ai/reference/provide-bf-shell.md +41 -0
- package/ai/reference/provide-bf-testing.md +33 -0
- package/ai/reference/reduced-transition.md +28 -0
- package/ai/reference/reduced_duration.md +17 -0
- package/ai/reference/resolve-shell-mode.md +33 -0
- package/ai/reference/scale-in.md +25 -0
- package/ai/reference/settle-duration.md +25 -0
- package/ai/reference/settle-pop.md +33 -0
- package/ai/reference/settle_easing.md +17 -0
- package/ai/reference/should-complete-pop.md +26 -0
- package/ai/reference/slide-down.md +26 -0
- package/ai/reference/slide-up.md +26 -0
- package/ai/reference/with-bf-shell-routing.md +19 -0
- package/bin/bf-ui.js +81 -0
- package/fesm2022/bitforgehq-ui-icons.mjs +230 -0
- package/fesm2022/bitforgehq-ui-icons.mjs.map +1 -0
- package/fesm2022/bitforgehq-ui-mobile.mjs +5530 -0
- package/fesm2022/bitforgehq-ui-mobile.mjs.map +1 -0
- package/fesm2022/bitforgehq-ui-number-input.mjs +680 -0
- package/fesm2022/bitforgehq-ui-number-input.mjs.map +1 -0
- package/fesm2022/bitforgehq-ui-overlay.mjs +2675 -0
- package/fesm2022/bitforgehq-ui-overlay.mjs.map +1 -0
- package/fesm2022/bitforgehq-ui-testing.mjs +402 -0
- package/fesm2022/bitforgehq-ui-testing.mjs.map +1 -0
- package/fesm2022/bitforgehq-ui.mjs +7765 -0
- package/fesm2022/bitforgehq-ui.mjs.map +1 -0
- package/llms-full.txt +25394 -0
- package/llms.txt +329 -0
- package/package.json +94 -0
- package/theme.css +253 -0
- package/types/bitforgehq-ui-icons.d.ts +50 -0
- package/types/bitforgehq-ui-mobile.d.ts +2914 -0
- package/types/bitforgehq-ui-number-input.d.ts +282 -0
- package/types/bitforgehq-ui-overlay.d.ts +1514 -0
- package/types/bitforgehq-ui-testing.d.ts +495 -0
- package/types/bitforgehq-ui.d.ts +3499 -0
|
@@ -0,0 +1,522 @@
|
|
|
1
|
+
# bf-toast
|
|
2
|
+
|
|
3
|
+
Passive status pill with an optional action that never steals focus
|
|
4
|
+
|
|
5
|
+
`BfToastComponent` from `@bitforgehq/ui/mobile` · Overlays · since 0.2.0
|
|
6
|
+
|
|
7
|
+
## Example
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { Component, inject, signal } from '@angular/core';
|
|
11
|
+
import { BfButtonComponent } from '@bitforgehq/ui';
|
|
12
|
+
import {
|
|
13
|
+
BfContentComponent,
|
|
14
|
+
BfHeaderComponent,
|
|
15
|
+
BfPageComponent,
|
|
16
|
+
type BfToastOptions,
|
|
17
|
+
BfToastService,
|
|
18
|
+
} from '@bitforgehq/ui/mobile';
|
|
19
|
+
|
|
20
|
+
/** `bf-toast` showcase: a bottom toast, a top toast and one with an Undo action. */
|
|
21
|
+
@Component({
|
|
22
|
+
selector: 'app-toast-showcase',
|
|
23
|
+
imports: [BfPageComponent, BfHeaderComponent, BfContentComponent, BfButtonComponent],
|
|
24
|
+
template: `
|
|
25
|
+
<bf-page data-showcase="toast">
|
|
26
|
+
<bf-header title="Toast" [largeTitle]="false" />
|
|
27
|
+
<bf-content>
|
|
28
|
+
<div class="flex flex-col gap-4 p-4">
|
|
29
|
+
<p class="text-caption text-text-muted">
|
|
30
|
+
Toasts queue: tap several buttons quickly and each shows once the one before it ends.
|
|
31
|
+
</p>
|
|
32
|
+
<bf-button (clicked)="show({ message: 'Saved' })">Bottom toast</bf-button>
|
|
33
|
+
<bf-button
|
|
34
|
+
variant="tonal"
|
|
35
|
+
(clicked)="show({ message: 'No connection', position: 'top' })"
|
|
36
|
+
>
|
|
37
|
+
Top toast
|
|
38
|
+
</bf-button>
|
|
39
|
+
<bf-button variant="outline" (clicked)="showWithUndo()">Toast with action</bf-button>
|
|
40
|
+
<p data-showcase-result class="text-body">{{ result() }}</p>
|
|
41
|
+
</div>
|
|
42
|
+
</bf-content>
|
|
43
|
+
</bf-page>
|
|
44
|
+
`,
|
|
45
|
+
})
|
|
46
|
+
export class ToastShowcasePage {
|
|
47
|
+
private readonly toasts = inject(BfToastService);
|
|
48
|
+
|
|
49
|
+
protected readonly result = signal('No toast yet.');
|
|
50
|
+
|
|
51
|
+
async show(options: BfToastOptions): Promise<void> {
|
|
52
|
+
const reason = await this.toasts.show(options).afterDismissed;
|
|
53
|
+
this.result.set(`“${options.message}” ended: ${reason}.`);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async showWithUndo(): Promise<void> {
|
|
57
|
+
const reason = await this.toasts.show({ message: 'Task deleted', action: { label: 'Undo' } })
|
|
58
|
+
.afterDismissed;
|
|
59
|
+
this.result.set(reason === 'action' ? 'Task restored.' : `“Task deleted” ended: ${reason}.`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Overview
|
|
65
|
+
|
|
66
|
+
The status pill that `BfToastService.show()` renders on the overlay core (`passive: true`, no
|
|
67
|
+
backdrop, no focus trap, no inert page and no back handler — it never steals focus): an ink
|
|
68
|
+
`bg-text text-bg rounded-full shadow-floating` pill holding the message (truncated to two lines)
|
|
69
|
+
and an optional action. The pill is not a live region: the service announces each toast once
|
|
70
|
+
through CDK `LiveAnnouncer`. The pointer over it or focus inside it pauses its timer, and
|
|
71
|
+
`Escape` with focus inside dismisses it. `position: 'bottom'` (default) sits 16 px above the
|
|
72
|
+
tab-bar inset (`--bf-tab-bar-inset-global`, published on `<html>` by `bf-tabs`, which leaves
|
|
73
|
+
8 px above the floating bar, so the toast clears it by 24 px) and the soft keyboard
|
|
74
|
+
(`--bf-toast-keyboard` on the host: `BfKeyboardService.height()` where the keyboard overlays
|
|
75
|
+
the WebView — native iOS — and 0 where the WebView already shrinks); `position: 'top'` sits
|
|
76
|
+
16 px below the status bar (a top margin of the safe-area inset plus 16 px, outside the pill)
|
|
77
|
+
instead. Not used directly in templates.
|
|
78
|
+
|
|
79
|
+
A status pill. `BfToastService.show(options)` renders `bf-toast` on the overlay core
|
|
80
|
+
(`passive: true`): an ink pill above the floating tab bar and the soft keyboard, with an optional
|
|
81
|
+
action. Only one toast is visible at a time — later calls queue and show once the current one
|
|
82
|
+
ends. You never place `<bf-toast>` in a template yourself.
|
|
83
|
+
|
|
84
|
+
## When to use
|
|
85
|
+
|
|
86
|
+
Use `BfToastService` in the Capacitor shell for a short status after an action, one at a time above the tab bar: "Saved", "Task deleted. Undo". Use [BfDesktopToastService](https://bitforge-ui.web.app/api/bf-desktop-toast-service) instead in a desktop web app, and [BfAlertDialogService](https://bitforge-ui.web.app/api/bf-alert-dialog-service) when the user must answer.
|
|
87
|
+
|
|
88
|
+
## Basic usage
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
import { Component, inject, signal } from '@angular/core';
|
|
92
|
+
import { BfButtonComponent } from '@bitforgehq/ui';
|
|
93
|
+
import { BfToastService } from '@bitforgehq/ui/mobile';
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* A toast and how it ended.
|
|
97
|
+
*
|
|
98
|
+
* `afterDismissed` resolves with the reason: `timeout`, `action` or `dismiss`.
|
|
99
|
+
*
|
|
100
|
+
* @preview none
|
|
101
|
+
*/
|
|
102
|
+
@Component({
|
|
103
|
+
selector: 'app-example-toast-basic',
|
|
104
|
+
imports: [BfButtonComponent],
|
|
105
|
+
template: `
|
|
106
|
+
<div class="flex flex-col items-start gap-3">
|
|
107
|
+
<bf-button (clicked)="open()">Save</bf-button>
|
|
108
|
+
<p aria-live="polite" class="text-caption text-text-muted">{{ result() }}</p>
|
|
109
|
+
</div>
|
|
110
|
+
`,
|
|
111
|
+
})
|
|
112
|
+
export class ToastBasicExampleComponent {
|
|
113
|
+
private readonly toasts = inject(BfToastService);
|
|
114
|
+
protected readonly result = signal('');
|
|
115
|
+
|
|
116
|
+
protected async open(): Promise<void> {
|
|
117
|
+
const ref = this.toasts.show({ message: 'Saved' });
|
|
118
|
+
const reason = await ref.afterDismissed; // 'timeout' | 'action' | 'dismiss'
|
|
119
|
+
this.result.set(`The toast ended: ${reason}.`);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
`dismiss()` ends the toast right away — closing it if it is the one currently shown, or dropping
|
|
125
|
+
it from the queue before it ever shows.
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
import { Component, inject, signal } from '@angular/core';
|
|
129
|
+
import { BfButtonComponent } from '@bitforgehq/ui';
|
|
130
|
+
import { BfToastService } from '@bitforgehq/ui/mobile';
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* A toast that stays until it is dismissed.
|
|
134
|
+
*
|
|
135
|
+
* `duration: 0` keeps "Uploading…" up; `dismiss()` ends it when the upload is done.
|
|
136
|
+
*
|
|
137
|
+
* @preview none
|
|
138
|
+
*/
|
|
139
|
+
@Component({
|
|
140
|
+
selector: 'app-example-toast-persistent',
|
|
141
|
+
imports: [BfButtonComponent],
|
|
142
|
+
template: `
|
|
143
|
+
<div class="flex flex-col items-start gap-3">
|
|
144
|
+
<bf-button (clicked)="open()">Upload</bf-button>
|
|
145
|
+
<p aria-live="polite" class="text-caption text-text-muted">{{ result() }}</p>
|
|
146
|
+
</div>
|
|
147
|
+
`,
|
|
148
|
+
})
|
|
149
|
+
export class ToastPersistentExampleComponent {
|
|
150
|
+
private readonly toasts = inject(BfToastService);
|
|
151
|
+
protected readonly result = signal('');
|
|
152
|
+
/** Stands in for the real upload. */
|
|
153
|
+
private upload(): Promise<void> {
|
|
154
|
+
return new Promise((resolve) => setTimeout(resolve, 1500));
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
protected async open(): Promise<void> {
|
|
158
|
+
const ref = this.toasts.show({ message: 'Uploading…', duration: 0 });
|
|
159
|
+
await this.upload();
|
|
160
|
+
ref.dismiss();
|
|
161
|
+
this.result.set('Uploaded.');
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
## Action
|
|
167
|
+
|
|
168
|
+
`action` renders a real, keyboard-reachable `<button>` inside the pill. Its `handler` runs before
|
|
169
|
+
the toast closes with reason `'action'`. A toast with an action stays until the action or
|
|
170
|
+
`dismiss()` ends it (see Duration), so keyboard and switch users can always reach it.
|
|
171
|
+
|
|
172
|
+
```ts
|
|
173
|
+
import { Component, inject, signal } from '@angular/core';
|
|
174
|
+
import { BfButtonComponent } from '@bitforgehq/ui';
|
|
175
|
+
import { BfToastService } from '@bitforgehq/ui/mobile';
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* A toast with an Undo action.
|
|
179
|
+
*
|
|
180
|
+
* The action's `handler` runs before the toast closes with reason `action`.
|
|
181
|
+
*
|
|
182
|
+
* @preview none
|
|
183
|
+
*/
|
|
184
|
+
@Component({
|
|
185
|
+
selector: 'app-example-toast-action',
|
|
186
|
+
imports: [BfButtonComponent],
|
|
187
|
+
template: `
|
|
188
|
+
<div class="flex flex-col items-start gap-3">
|
|
189
|
+
<bf-button variant="danger" (clicked)="open()">Delete task</bf-button>
|
|
190
|
+
<p aria-live="polite" class="text-caption text-text-muted">{{ result() }}</p>
|
|
191
|
+
</div>
|
|
192
|
+
`,
|
|
193
|
+
})
|
|
194
|
+
export class ToastActionExampleComponent {
|
|
195
|
+
private readonly toasts = inject(BfToastService);
|
|
196
|
+
protected readonly result = signal('');
|
|
197
|
+
|
|
198
|
+
protected open(): void {
|
|
199
|
+
this.result.set('Task deleted.');
|
|
200
|
+
this.toasts.show({
|
|
201
|
+
message: 'Task deleted',
|
|
202
|
+
action: { label: 'Undo', handler: () => this.result.set('Task restored.') },
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## Duration
|
|
209
|
+
|
|
210
|
+
`duration` (ms) is how long the toast stays before it auto-dismisses, resolving `'timeout'`. It
|
|
211
|
+
defaults to `3000` for a toast without an action and to `0` for a toast with one: an action toast
|
|
212
|
+
persists until its action or `dismiss()` ends it, because an "Undo" that vanishes after 3 s is out
|
|
213
|
+
of reach for many keyboard, switch and screen-reader users (WCAG 2.2.1 Timing Adjustable). An
|
|
214
|
+
explicit `duration` always wins. `duration: 0` also suits a toast that tracks an ongoing
|
|
215
|
+
operation (an upload, a sync) rather than a one-off confirmation.
|
|
216
|
+
|
|
217
|
+
```ts
|
|
218
|
+
import { Component, inject, signal } from '@angular/core';
|
|
219
|
+
import { BfButtonComponent } from '@bitforgehq/ui';
|
|
220
|
+
import { BfToastService } from '@bitforgehq/ui/mobile';
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* An action toast with a timeout.
|
|
224
|
+
*
|
|
225
|
+
* An action toast persists until Undo or `dismiss()`; `duration` opts it back into a timeout.
|
|
226
|
+
*
|
|
227
|
+
* @preview none
|
|
228
|
+
*/
|
|
229
|
+
@Component({
|
|
230
|
+
selector: 'app-example-toast-duration',
|
|
231
|
+
imports: [BfButtonComponent],
|
|
232
|
+
template: `
|
|
233
|
+
<div class="flex flex-col items-start gap-3">
|
|
234
|
+
<bf-button (clicked)="open()">Delete task</bf-button>
|
|
235
|
+
<p aria-live="polite" class="text-caption text-text-muted">{{ result() }}</p>
|
|
236
|
+
</div>
|
|
237
|
+
`,
|
|
238
|
+
})
|
|
239
|
+
export class ToastDurationExampleComponent {
|
|
240
|
+
private readonly toasts = inject(BfToastService);
|
|
241
|
+
protected readonly result = signal('');
|
|
242
|
+
|
|
243
|
+
protected open(): void {
|
|
244
|
+
const undo = (): void => this.result.set('Task restored.');
|
|
245
|
+
this.result.set('Task deleted.');
|
|
246
|
+
this.toasts.show({
|
|
247
|
+
message: 'Task deleted',
|
|
248
|
+
action: { label: 'Undo', handler: undo },
|
|
249
|
+
duration: 8000,
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
The timer pauses while the pointer is over the toast or focus is inside it, and resumes with the
|
|
256
|
+
time that was left once both have left — a toast never times out under the reader's pointer or
|
|
257
|
+
while its action is focused.
|
|
258
|
+
|
|
259
|
+
## Position
|
|
260
|
+
|
|
261
|
+
`position` is `'bottom'` (default) or `'top'`. `'bottom'` sits 16 px above the tab-bar inset
|
|
262
|
+
(`--bf-tab-bar-inset-global`, published on `<html>` by `bf-tabs`; it already leaves 8 px above the
|
|
263
|
+
floating bar, so the toast clears the bar by 24 px) and the soft keyboard,
|
|
264
|
+
recomputed live as either changes — `BfKeyboardService.height()` where the keyboard overlays the
|
|
265
|
+
WebView (native iOS), nothing where the WebView already shrinks for it (Android, the web); `'top'` sits 16 px below the status bar (a top margin of the safe-area inset plus 16 px), for a toast that would
|
|
266
|
+
otherwise sit under a keyboard-focused control.
|
|
267
|
+
|
|
268
|
+
```ts
|
|
269
|
+
import { Component, inject } from '@angular/core';
|
|
270
|
+
import { BfButtonComponent } from '@bitforgehq/ui';
|
|
271
|
+
import { BfToastService } from '@bitforgehq/ui/mobile';
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* A toast at the top of the screen.
|
|
275
|
+
*
|
|
276
|
+
* `position: 'top'` shows it under the status bar instead of above the tab bar.
|
|
277
|
+
*
|
|
278
|
+
* @preview none
|
|
279
|
+
*/
|
|
280
|
+
@Component({
|
|
281
|
+
selector: 'app-example-toast-position',
|
|
282
|
+
imports: [BfButtonComponent],
|
|
283
|
+
template: `<bf-button (clicked)="open()">Go offline</bf-button>`,
|
|
284
|
+
})
|
|
285
|
+
export class ToastPositionExampleComponent {
|
|
286
|
+
private readonly toasts = inject(BfToastService);
|
|
287
|
+
|
|
288
|
+
protected open(): void {
|
|
289
|
+
this.toasts.show({ message: 'No connection', position: 'top' });
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
## Queueing
|
|
295
|
+
|
|
296
|
+
`show()` never interrupts a visible timed toast: a second call while one is up queues in order and
|
|
297
|
+
shows once the first ends (however it ends — timeout, action or `dismiss()`). Each call still
|
|
298
|
+
returns its own `BfToastRef` immediately, so callers can `dismiss()` or await `afterDismissed` on a
|
|
299
|
+
toast that hasn't shown yet.
|
|
300
|
+
|
|
301
|
+
Persistent toasts (`duration: 0`, which is the default for a toast with an action) never block the
|
|
302
|
+
queue: the next `show()` replaces one, visible or still waiting, and it resolves `'dismiss'` — a
|
|
303
|
+
superseded "Undo" is stale anyway (the Material snackbar model). A navigation (a push, a pop, a
|
|
304
|
+
tab switch) never closes a toast, so a "Saved" survives the navigation that follows the save. Keep
|
|
305
|
+
the ref of an action toast and `dismiss()` it yourself as soon as it goes stale, for example when the user navigates away or
|
|
306
|
+
takes the next action.
|
|
307
|
+
|
|
308
|
+
```ts
|
|
309
|
+
import { Component, inject, signal } from '@angular/core';
|
|
310
|
+
import { BfButtonComponent } from '@bitforgehq/ui';
|
|
311
|
+
import { type BfToastRef, BfToastService } from '@bitforgehq/ui/mobile';
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* One Undo toast at a time.
|
|
315
|
+
*
|
|
316
|
+
* Each archive dismisses the previous Undo toast before it shows its own.
|
|
317
|
+
*
|
|
318
|
+
* @preview none
|
|
319
|
+
*/
|
|
320
|
+
@Component({
|
|
321
|
+
selector: 'app-example-toast-queueing',
|
|
322
|
+
imports: [BfButtonComponent],
|
|
323
|
+
template: `
|
|
324
|
+
<div class="flex flex-col items-start gap-3">
|
|
325
|
+
<bf-button (clicked)="open()">Archive</bf-button>
|
|
326
|
+
<p aria-live="polite" class="text-caption text-text-muted">{{ result() }}</p>
|
|
327
|
+
</div>
|
|
328
|
+
`,
|
|
329
|
+
})
|
|
330
|
+
export class ToastQueueingExampleComponent {
|
|
331
|
+
private readonly toasts = inject(BfToastService);
|
|
332
|
+
protected readonly result = signal('');
|
|
333
|
+
private undo: BfToastRef | null = null;
|
|
334
|
+
|
|
335
|
+
protected open(): void {
|
|
336
|
+
this.undo?.dismiss();
|
|
337
|
+
this.undo = this.toasts.show({
|
|
338
|
+
message: 'Archived',
|
|
339
|
+
action: { label: 'Undo', handler: () => this.result.set('Restored.') },
|
|
340
|
+
});
|
|
341
|
+
this.result.set('Archived.');
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
## Desktop
|
|
347
|
+
|
|
348
|
+
`BfToastService` shows one phone-width toast at a time and is meant for the Capacitor shell
|
|
349
|
+
(`provideBfShell()`). On a desktop web app, use `BfDesktopToastService` from
|
|
350
|
+
`@bitforgehq/ui/overlay` instead: it needs only `@angular/cdk`, no Capacitor and no router, stacks
|
|
351
|
+
up to three cards in the bottom-right corner and follows the same rules (timing, actions, replacing,
|
|
352
|
+
one announcement per toast). A toast never locks the page's scroll or makes it `inert`.
|
|
353
|
+
|
|
354
|
+
```ts
|
|
355
|
+
import { Component, inject, signal } from '@angular/core';
|
|
356
|
+
import { BfButtonComponent } from '@bitforgehq/ui';
|
|
357
|
+
import { BfDesktopToastService } from '@bitforgehq/ui/overlay';
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* A toast and how it ended.
|
|
361
|
+
*
|
|
362
|
+
* `afterDismissed` resolves with the reason: `timeout`, `action` or `dismiss`.
|
|
363
|
+
*/
|
|
364
|
+
@Component({
|
|
365
|
+
selector: 'app-example-desktop-toast-basic',
|
|
366
|
+
imports: [BfButtonComponent],
|
|
367
|
+
template: `
|
|
368
|
+
<div class="flex flex-col items-start gap-3">
|
|
369
|
+
<bf-button (clicked)="save()">Save</bf-button>
|
|
370
|
+
<p aria-live="polite" class="text-caption text-text-muted">{{ result() }}</p>
|
|
371
|
+
</div>
|
|
372
|
+
`,
|
|
373
|
+
})
|
|
374
|
+
export class DesktopToastBasicExampleComponent {
|
|
375
|
+
private readonly toasts = inject(BfDesktopToastService);
|
|
376
|
+
protected readonly result = signal('');
|
|
377
|
+
|
|
378
|
+
protected async save(): Promise<void> {
|
|
379
|
+
const ref = this.toasts.show({ message: 'Changes saved' });
|
|
380
|
+
const reason = await ref.afterDismissed; // 'timeout' | 'action' | 'dismiss'
|
|
381
|
+
this.result.set(`The toast ended: ${reason}.`);
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
## Theming
|
|
387
|
+
|
|
388
|
+
- Pill: `bg-text text-bg` (the ink surface, inverted from the rest of the UI), `rounded-full`,
|
|
389
|
+
`shadow-floating`.
|
|
390
|
+
- Action label: `text-bg` with an underline — `bf-button`'s `text` variant uses `text-primary`,
|
|
391
|
+
which has no contrast against the ink pill, so the action is a plain styled `<button>` instead.
|
|
392
|
+
Its hit area is 44 px tall (`min-h-hit`) and reaches into the pill's padding with negative margins,
|
|
393
|
+
so the pill keeps its 48 px height and its width. It shows a pointer cursor and fades to 80 %
|
|
394
|
+
opacity on hover.
|
|
395
|
+
- Width: at most the viewport minus the 16 px gutter on each side.
|
|
396
|
+
|
|
397
|
+
## Accessibility
|
|
398
|
+
|
|
399
|
+
- Announcement: `BfToastService` announces each toast exactly once, politely, through the CDK
|
|
400
|
+
`LiveAnnouncer` (a persistent live region that exists before the toast does) as the toast
|
|
401
|
+
shows: the message, followed by the action label when there is one ("Task deleted. Undo"). A
|
|
402
|
+
queued toast is announced when it shows, never while it waits. The pill itself is not a live
|
|
403
|
+
region (no `role`, no `aria-live`), and the overlay pane around it has no role either, so
|
|
404
|
+
nothing is read twice.
|
|
405
|
+
- Focus: the toast is opened `passive` on the overlay core: no backdrop, no focus trap, the page
|
|
406
|
+
behind it is never made `inert`, and it never moves focus — the action button is reachable by
|
|
407
|
+
Tab like any other control on the page.
|
|
408
|
+
- Keyboard: `Escape` while focus is inside the toast dismisses it (resolving `'dismiss'`); the key
|
|
409
|
+
is claimed there, so it never also reaches a dialog. `Escape` elsewhere on the page is not the
|
|
410
|
+
toast's. There is no close button.
|
|
411
|
+
- Focus return: when a toast that still holds focus closes for any reason (its action, `Escape`,
|
|
412
|
+
`dismiss()`, a replacing `show()`), focus returns, with `preventScroll`, to the element focused
|
|
413
|
+
last outside the toast — the one focused when it opened, or one focused since — if that element
|
|
414
|
+
is still in the page. Once focus has fallen to the page body (a click on empty space) there is no
|
|
415
|
+
such element and focus is not moved. A toast that no longer holds focus leaves it alone, and an
|
|
416
|
+
action `handler` that moves focus elsewhere keeps that. A mouse click on the action focuses it
|
|
417
|
+
in most browsers, so it returns focus the same way.
|
|
418
|
+
- Timing: action toasts persist by default, and every toast pauses while hovered or focused (see
|
|
419
|
+
Duration).
|
|
420
|
+
- Only one toast is ever visible, so announcements don't overlap. Under `prefers-reduced-motion` it
|
|
421
|
+
fades in and out without the 12 px slide.
|
|
422
|
+
|
|
423
|
+
### Keyboard
|
|
424
|
+
|
|
425
|
+
| Key | Action |
|
|
426
|
+
| ----------------------------------- | --------------------------------------------------------------------------------------- |
|
|
427
|
+
| <kbd>Tab</kbd> | Reaches the action button in page order, like any control; the toast never takes focus |
|
|
428
|
+
| <kbd>Enter</kbd> / <kbd>Space</kbd> | Runs the focused action; the toast closes with `'action'` |
|
|
429
|
+
| <kbd>Escape</kbd> | With focus inside the toast: dismisses it (`'dismiss'`); a dialog underneath stays open |
|
|
430
|
+
|
|
431
|
+
## Notes
|
|
432
|
+
|
|
433
|
+
### Do
|
|
434
|
+
|
|
435
|
+
- Keep the message short — it truncates to two lines.
|
|
436
|
+
- Keep the ref of a persistent toast and `dismiss()` it once it is stale (navigation, the next
|
|
437
|
+
action); the next `show()` replaces it anyway, but until then it stays up.
|
|
438
|
+
- Give `action.handler` a fast, synchronous-feeling effect (undo, retry) — the toast closes right
|
|
439
|
+
after it runs.
|
|
440
|
+
- Leave action toasts on their default (no timeout); if you set a `duration` on one, make it
|
|
441
|
+
generous and offer the same action somewhere persistent too.
|
|
442
|
+
|
|
443
|
+
### Don't
|
|
444
|
+
|
|
445
|
+
- Don't rely on the toast for anything the user must see — it can be missed, it waits in the queue
|
|
446
|
+
behind the one already up, and a persistent toast is replaced by the next `show()`.
|
|
447
|
+
- Don't put more than one action in the pill; it holds a single action button.
|
|
448
|
+
- Don't use a toast for anything the user must actively respond to before continuing — it never
|
|
449
|
+
traps focus or blocks the page; reach for a dialog or sheet instead.
|
|
450
|
+
|
|
451
|
+
## Migration notes
|
|
452
|
+
|
|
453
|
+
### 0.3.0
|
|
454
|
+
|
|
455
|
+
The toast engine (timing, hold, replace, focus return, announcements, teardown) moved into
|
|
456
|
+
`@bitforgehq/ui/overlay`, shared with the new desktop toast; `BfToastService` renders one toast at
|
|
457
|
+
a time on it exactly as before. `BfToastRef`, `BfToastDismissReason` and `BfToastAction` are now
|
|
458
|
+
also exported from `@bitforgehq/ui/overlay`; the `/mobile` exports stay (not deprecated) and are
|
|
459
|
+
the same types and class. No behaviour change: a navigation never closes a toast (it opts out of
|
|
460
|
+
the core's new `closeOnNavigation`).
|
|
461
|
+
|
|
462
|
+
JSDoc of `toast.component.ts` for the API reference: `@summary`/`@since` on the exported symbols. No
|
|
463
|
+
behaviour change.
|
|
464
|
+
|
|
465
|
+
JSDoc of `toast.service.ts` for the API reference: `@summary`/`@since` on the exported symbols;
|
|
466
|
+
`@default` on the option properties the code applies; `@param` docs. No behaviour change.
|
|
467
|
+
|
|
468
|
+
**Behaviour:** `--bf-toast-keyboard` is written in the render phase (`afterRenderEffect`) instead
|
|
469
|
+
of an `effect`; internal timing only, no API change.
|
|
470
|
+
|
|
471
|
+
### 0.2.0
|
|
472
|
+
|
|
473
|
+
New `BfToastService.show({ message, action?, duration, position })` in `@bitforgehq/ui/mobile`, a
|
|
474
|
+
queued status pill on the overlay core opened `passive: true` (no backdrop, no focus trap, the page
|
|
475
|
+
stays interactive and it never counts towards `BfOverlayStack.count()`). Only one toast shows at a
|
|
476
|
+
time; later `show()` calls queue in order behind a timed toast and appear once it ends (a persistent
|
|
477
|
+
one gives way, see the behaviour changes below). It returns a `BfToastRef` (`dismiss()`,
|
|
478
|
+
`afterDismissed: Promise<'timeout' | 'action' | 'dismiss'>`) — the toast resolves `'timeout'` after
|
|
479
|
+
`duration` ms (`0` keeps it up until `dismiss()` or its action; the default is 3000 without an
|
|
480
|
+
action and 0 with one, see below), `'action'` when the optional action button is clicked (its
|
|
481
|
+
`handler` runs first), and `'dismiss'` from `dismiss()`, whether the toast is showing or still
|
|
482
|
+
queued. `bf-toast` is an ink `bg-text text-bg rounded-full shadow-floating` pill (message
|
|
483
|
+
`line-clamp-2`, announced through `LiveAnnouncer`, see the "Changed" note below); the action renders
|
|
484
|
+
as a plain underlined `text-bg` button rather than `bf-button`'s `text` variant, which has no
|
|
485
|
+
contrast on the ink surface. `position: 'bottom'` (default) keeps 16 px clear of the floating tab
|
|
486
|
+
bar (`--bf-tab-bar-inset-global` on `<html>`, see `### tabs`) and the soft keyboard, written live as
|
|
487
|
+
`--bf-toast-keyboard`: `BfKeyboardService.height()` where the keyboard overlays the WebView (native
|
|
488
|
+
iOS), `0` where the WebView already resizes (Android, the web); `position: 'top'` sits 16 px below
|
|
489
|
+
the status bar instead, as a top margin (`mt-[calc(var(--spacing-safe-top)+var(--spacing)*4)]`)
|
|
490
|
+
outside the pill: an earlier `pt-safe-top` on the host overrode the pill's own `py-3` (0 top padding
|
|
491
|
+
on the web, about 50 px on a notched iPhone). Enter fades in with a 12 px slide from its anchored
|
|
492
|
+
edge, exit is a plain fade; both collapse to a fade-only under `prefers-reduced-motion`.
|
|
493
|
+
`provideBfShell()` now also provides `BfToastService`. The action button has a 44 px hit area
|
|
494
|
+
(`min-h-hit px-3`) that reaches into the pill's padding (`-my-2.5 -mx-3`), so the pill keeps its 48
|
|
495
|
+
px height and width.
|
|
496
|
+
|
|
497
|
+
Fixed: the action button shows a pointer cursor and fades to 80 % opacity on hover.
|
|
498
|
+
|
|
499
|
+
Changed: with the new tab-bar inset (see `### tabs`, which now includes 8 px above the bar) a
|
|
500
|
+
bottom toast sits 16 px above the inset and so clears the floating tab bar by 24 px (was 16 px).
|
|
501
|
+
|
|
502
|
+
Behaviour change: a toast with an `action` no longer auto-dismisses by default (`duration`
|
|
503
|
+
defaults to `0` with an action, still `3000` without one; an explicit `duration` wins), so an
|
|
504
|
+
"Undo" stays reachable for keyboard, switch and screen-reader users (WCAG 2.2.1). Every toast's
|
|
505
|
+
timer pauses while the pointer is over it or focus is inside it and resumes with the remaining
|
|
506
|
+
time. When a toast that holds focus closes (action or `dismiss()`), focus returns with
|
|
507
|
+
`preventScroll` to the element it came from (the first `focusin`'s `relatedTarget`, else the
|
|
508
|
+
element focused when it opened), if still in the page; before, it fell to `<body>`.
|
|
509
|
+
|
|
510
|
+
Changed: toasts are announced through CDK `LiveAnnouncer` (`announce(text, 'polite')`, once per
|
|
511
|
+
toast as it shows; the text is the message plus the action label). The `bf-toast` host no longer
|
|
512
|
+
carries `role="status"`/`aria-live="polite"`: a live region inserted together with its text was
|
|
513
|
+
not reliably announced. Tests locating the pill by `role="status"` should use `bf-toast`.
|
|
514
|
+
|
|
515
|
+
Behaviour change: persistent toasts (`duration` 0, the default with an action) never block the
|
|
516
|
+
queue — the next `show()` dismisses one, visible or still queued, resolving `'dismiss'` (the
|
|
517
|
+
Material snackbar model; a timed toast still queues the next one). `Escape` while focus is inside a
|
|
518
|
+
toast dismisses it (`'dismiss'`) and is claimed there. Focus returns only while the toast still
|
|
519
|
+
holds focus at close time, to the element focused last outside it; once focus has fallen to the
|
|
520
|
+
page body it is not moved to the element that was focused when the toast opened. The service ends
|
|
521
|
+
every toast (timers and document listeners included) when its injector is destroyed, and the
|
|
522
|
+
announcement joins a message ending in `:`, `;` or `,` to the action label with a space.
|