@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,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.
|