@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,299 @@
1
+ # bf-page
2
+
3
+ Frame for one stacked mobile screen that supplies lifecycle events and shared page state
4
+
5
+ `BfPageComponent` from `@bitforgehq/ui/mobile` · Navigation · since 0.2.0
6
+
7
+ ## Example
8
+
9
+ ```ts
10
+ import { Component, inject, signal } from '@angular/core';
11
+ import { BfButtonComponent, BfListComponent, BfListItemComponent } from '@bitforgehq/ui';
12
+ import {
13
+ BfContentComponent,
14
+ BfHeaderComponent,
15
+ BfNavController,
16
+ BfPageComponent,
17
+ } from '@bitforgehq/ui/mobile';
18
+
19
+ /** `bf-page` showcase: logs this page's lifecycle events and shows its view state. */
20
+ @Component({
21
+ selector: 'app-page-showcase',
22
+ imports: [
23
+ BfPageComponent,
24
+ BfHeaderComponent,
25
+ BfContentComponent,
26
+ BfButtonComponent,
27
+ BfListComponent,
28
+ BfListItemComponent,
29
+ ],
30
+ template: `
31
+ <bf-page
32
+ #page
33
+ data-showcase="page"
34
+ (bfViewWillEnter)="log('bfViewWillEnter')"
35
+ (bfViewDidEnter)="log('bfViewDidEnter')"
36
+ (bfViewWillLeave)="log('bfViewWillLeave')"
37
+ (bfViewDidLeave)="log('bfViewDidLeave')"
38
+ >
39
+ <bf-header title="Page" [largeTitle]="false" />
40
+ <bf-content>
41
+ <div class="flex flex-col gap-4 p-4">
42
+ <p class="text-caption text-text-muted">
43
+ Push a page, then go back: this page stays alive underneath and logs all four lifecycle
44
+ events.
45
+ </p>
46
+ <p data-showcase-result class="text-body">View state: {{ page.viewState() }}</p>
47
+ <bf-button (clicked)="push()">Push a page</bf-button>
48
+ <bf-list [inset]="false">
49
+ @for (entry of events(); track $index) {
50
+ <bf-list-item [title]="entry" [subtitle]="'Event ' + ($index + 1)" />
51
+ }
52
+ </bf-list>
53
+ </div>
54
+ </bf-content>
55
+ </bf-page>
56
+ `,
57
+ })
58
+ export class PageShowcasePage {
59
+ private readonly nav = inject(BfNavController);
60
+
61
+ protected readonly events = signal<readonly string[]>([]);
62
+
63
+ log(event: string): void {
64
+ this.events.update((events) => [...events, event]);
65
+ }
66
+
67
+ push(): void {
68
+ void this.nav.forward('/showcase/page/next');
69
+ }
70
+ }
71
+ ```
72
+
73
+ ## Overview
74
+
75
+ The page frame of a mobile app: `bf-stack-outlet` positions one per stacked page and provides
76
+ its `BfViewLifecycle` through the element injector. Everything scrollable inside a page goes
77
+ into `bf-content`; `bf-page` is only the frame, the lifecycle signal and a per-page
78
+ `BfPageContext` (provided for its children, where `bf-content` registers its scroller). The
79
+ page an outlet shows makes its `bf-content` the document's `main` landmark. Used
80
+ standalone (outside an outlet, e.g. in a docs demo) it falls back to a local, inert lifecycle
81
+ instance instead of throwing, and exposes no `main` landmark.
82
+
83
+ The page frame of a mobile app. `bf-stack-outlet` creates one wrapper frame per stacked page and
84
+ positions `bf-page` to fill it; use one `bf-page` as the root element of every routed page
85
+ component. Everything scrollable inside a page goes into `bf-content` — `bf-page` itself does not
86
+ scroll, it is just the frame, the lifecycle signal and the per-page `BfPageContext`.
87
+
88
+ ## When to use
89
+
90
+ Use `bf-page` as the root element of every routed page under a [bf-stack-outlet](https://bitforge-ui.web.app/components/stack-outlet), for the frame and the view lifecycle. Use a plain page component instead under a normal `router-outlet` on desktop.
91
+
92
+ ## Basic usage
93
+
94
+ ```ts
95
+ import { Component } from '@angular/core';
96
+ import { BfPageComponent } from '@bitforgehq/ui/mobile';
97
+
98
+ /**
99
+ * A page of the stack.
100
+ *
101
+ * `data-bf-page-title` marks the heading the page is named by.
102
+ *
103
+ * @preview none
104
+ */
105
+ @Component({
106
+ selector: 'app-example-page-basic',
107
+ imports: [BfPageComponent],
108
+ template: `
109
+ <bf-page>
110
+ <h1 data-bf-page-title>Item</h1>
111
+ <p>Page content</p>
112
+ </bf-page>
113
+ `,
114
+ })
115
+ export class PageBasicExampleComponent {}
116
+ ```
117
+
118
+ ## Lifecycle
119
+
120
+ `bf-page` injects the `BfViewLifecycle` the outlet provides for this page and re-exposes it as
121
+ `(bfViewWillEnter)`, `(bfViewDidEnter)`, `(bfViewWillLeave)`, `(bfViewDidLeave)` and as
122
+ `viewState()` (`'entering' | 'active' | 'leaving' | 'inactive'`). Load or refresh data on
123
+ `bfViewDidEnter` rather than the component's constructor, so a page parked in the stack and later
124
+ returned to (a pop landing on it) reloads only if you choose to, and a page pushed back on top of
125
+ itself (rare, but possible with `nav.root()`) reloads correctly.
126
+
127
+ The events are delivered synchronously as the outlet fires them: a new page — the app's first page,
128
+ a pushed page, the page `nav.root()` creates — gets `bfViewWillEnter` and then `bfViewDidEnter`
129
+ once its transition settles (at once with `[animated]="false"`), and a popped or root-replaced page
130
+ gets `bfViewWillLeave` and `bfViewDidLeave` before it is destroyed. A `bf-page` rendered later than
131
+ its routed component's first render (inside an `@if` or `@defer`) misses the events that fired
132
+ before it existed; read `viewState()` there instead.
133
+
134
+ ```ts
135
+ import { Component, signal } from '@angular/core';
136
+ import { BfPageComponent } from '@bitforgehq/ui/mobile';
137
+
138
+ /**
139
+ * Loading when the page has entered.
140
+ *
141
+ * `bfViewDidEnter` fires each time the page becomes the visible one, also on the way back.
142
+ *
143
+ * @preview none
144
+ */
145
+ @Component({
146
+ selector: 'app-example-page-lifecycle',
147
+ imports: [BfPageComponent],
148
+ template: `
149
+ <bf-page (bfViewDidEnter)="load()">
150
+ <p>{{ item()?.name }}</p>
151
+ </bf-page>
152
+ `,
153
+ })
154
+ export class PageLifecycleExampleComponent {
155
+ protected readonly item = signal<{ name: string } | null>(null);
156
+
157
+ protected load(): void {
158
+ this.item.set({ name: 'Atlas rebrand' });
159
+ }
160
+ }
161
+ ```
162
+
163
+ Used outside a `bf-stack-outlet` — for example in a docs demo, or a unit test that renders the
164
+ page component directly — `bf-page` falls back to a local, inert `BfViewLifecycle` instead of
165
+ throwing; its outputs simply never fire.
166
+
167
+ ## Page context
168
+
169
+ `bf-page` provides a fresh `BfPageContext` to everything inside it. `bf-content` registers its
170
+ scroll container there (`scroller`, a `signal<HTMLElement | null>`) on init and clears it on
171
+ destroy, so a sibling such as a header can follow the page's scroll without querying the DOM.
172
+ `mainLandmark` (a `signal(true)`) makes that scroller the document's `main` landmark. It stays
173
+ true for the page a `bf-stack-outlet` shows; a `bf-page` outside an outlet (a docs illustration)
174
+ or nested in another page sets it to false, and so does `bf-modal` for its content.
175
+
176
+ ```ts
177
+ import { Component, computed, inject } from '@angular/core';
178
+ import { BfContentComponent, BfPageComponent, BfPageContext } from '@bitforgehq/ui/mobile';
179
+
180
+ /** A bar that follows the page's scroller through the page context. */
181
+ @Component({
182
+ selector: 'app-scroll-hint',
183
+ template: `<p class="text-caption text-text-muted p-4">{{ hint() }}</p>`,
184
+ })
185
+ export class ScrollHintComponent {
186
+ private readonly scroller = inject(BfPageContext, { optional: true })?.scroller;
187
+ protected readonly hint = computed(() =>
188
+ this.scroller?.() ? 'The page scrolls in bf-content.' : 'No scroller yet.',
189
+ );
190
+ }
191
+
192
+ /**
193
+ * The page context.
194
+ *
195
+ * `bf-content` registers its scroller in the page's `BfPageContext`, so a sibling reads it
196
+ * without querying the DOM.
197
+ *
198
+ * @preview none
199
+ */
200
+ @Component({
201
+ selector: 'app-example-page-context',
202
+ imports: [BfContentComponent, BfPageComponent, ScrollHintComponent],
203
+ template: `
204
+ <bf-page>
205
+ <app-scroll-hint />
206
+ <bf-content>
207
+ <p class="p-4">Page content</p>
208
+ </bf-content>
209
+ </bf-page>
210
+ `,
211
+ })
212
+ export class PageContextExampleComponent {}
213
+ ```
214
+
215
+ ## Desktop
216
+
217
+ `bf-page` is part of the Capacitor shell (`provideBfShell()`). A desktop web app routes plain page components through `router-outlet` and uses Angular's own lifecycle hooks; `bfViewDidEnter` and the other view events exist only because the stack keeps pages alive.
218
+
219
+ ## Theming
220
+
221
+ - Background: `bg` (the page's own frame, so a sliding page never shows the page under it through
222
+ transparent areas).
223
+ - Layout: `absolute inset-0 flex flex-col`, filling whatever frame the outlet gives it (or, used
224
+ standalone, its containing block).
225
+
226
+ ## Accessibility
227
+
228
+ `bf-page` itself carries no `tabindex`; on push, the outlet moves focus to the page's title — the
229
+ element marked `data-bf-page-title` (the `bf-header` title once it exists) — or, when there is no
230
+ such element, to the positioned wrapper the outlet creates around the page. Give every page one
231
+ element marked `data-bf-page-title` so a screen reader announces where it landed. The page's
232
+ `bf-content` is the document's `main` landmark while the page is shown; a parked page is
233
+ `aria-hidden`, so exactly one `main` is exposed.
234
+
235
+ ## Outputs
236
+
237
+ | Name | Payload | Description |
238
+ | ----------------- | ------- | ----------------------------------------------------------- |
239
+ | `bfViewWillEnter` | `void` | Emitted when the page starts entering the top of the stack. |
240
+ | `bfViewDidEnter` | `void` | Emitted once the page is active at the top of the stack. |
241
+ | `bfViewWillLeave` | `void` | Emitted when the page starts leaving the top of the stack. |
242
+ | `bfViewDidLeave` | `void` | Emitted once the page has left the top of the stack. |
243
+
244
+ ## Slots
245
+
246
+ - `default`: The page: a `bf-header` and a `bf-content`.
247
+
248
+ ## Notes
249
+
250
+ ### Do
251
+
252
+ - Use exactly one `bf-page` as the top-level element of a routed page component.
253
+ - Load data in `bfViewDidEnter`, not the constructor, so a returned-to page can choose whether to
254
+ reload.
255
+ - Mark the page's heading with `data-bf-page-title` so push focuses it.
256
+
257
+ ### Don't
258
+
259
+ - Don't put scrollable content directly inside `bf-page`; wrap it in `bf-content`.
260
+ - Don't set a `tabindex` on `bf-page` yourself; the outlet manages focus on its wrapper.
261
+
262
+ ## Migration notes
263
+
264
+ ### 0.3.0
265
+
266
+ JSDoc of `page-context.ts` for the API reference: `@summary`/`@since` on the exported symbols. No
267
+ behaviour change.
268
+
269
+ JSDoc of `page.component.ts` for the API reference: `@slot` docs for `default`. No behaviour change.
270
+
271
+ `BfPageContext` gains `safeAreaTop` (a `signal(true)`): whether the page reaches the top of the
272
+ screen, so its header and a `fullscreen` content pad the top safe area. `bf-modal`'s page-sheet
273
+ presentation sets it to false.
274
+
275
+ `BfPageContext` gains `mainLandmark` (a `signal(true)`): whether the page's `bf-content` is the
276
+ `main` landmark. A `bf-page` outside a `bf-stack-outlet` (a docs illustration) or nested in
277
+ another page sets it to false, so the document keeps one `main` (Migration 36).
278
+
279
+ ### 0.2.0
280
+
281
+ New `bf-page`: the page frame (`absolute inset-0 flex flex-col bg-bg`) that `bf-stack-outlet`
282
+ positions one of per stacked page. Injects the per-page `BfViewLifecycle` the outlet provides
283
+ (falling back to a local, inert instance when used standalone, e.g. in a docs demo) and re-exposes
284
+ it as `viewState` (`computed`) plus outputs `bfViewWillEnter`, `bfViewDidEnter`, `bfViewWillLeave`,
285
+ `bfViewDidLeave`, delivered synchronously through `BfViewLifecycle.on()`: a new page gets
286
+ `bfViewWillEnter` and `bfViewDidEnter` (also the first page, without animation and after
287
+ `nav.root()`), and a popped or root-replaced page gets `bfViewWillLeave` and `bfViewDidLeave` before
288
+ it is destroyed. Carries no `tabindex` itself; the outlet still owns push/pop focus
289
+ management on its own wrapper.
290
+
291
+ `bf-page` now provides a per-page `BfPageContext` (`providers: [BfPageContext]`) whose `scroller`
292
+ signal (`HTMLElement | null`) is where `bf-content` registers its scroll container on init and
293
+ clears it on destroy, so `bf-header` can follow the page's scroll without querying the DOM. Its
294
+ `headerHeight` signal (px, 0 by default) is where a `translucent` `bf-header` publishes its
295
+ expanded height for `bf-content` to pad by.
296
+
297
+ Inside `bf-stack-outlet`, the outermost `bf-page` of a stacked page also registers its
298
+ `BfPageContext` with the outlet (a nested `bf-page` leaves it), so the outlet can scroll the page's
299
+ content to the top when its tab is re-selected.
@@ -0,0 +1,215 @@
1
+ # bf-progress
2
+
3
+ Progress bar in two sizes and four tones for completion, quotas and unknown waits
4
+
5
+ `BfProgressComponent` from `@bitforgehq/ui` · Data display · since 0.1.0
6
+
7
+ ## Example
8
+
9
+ ```ts
10
+ import { Component, signal } from '@angular/core';
11
+ import { BfButtonComponent, BfProgressComponent } from '@bitforgehq/ui';
12
+
13
+ /**
14
+ * The bf-progress overview.
15
+ *
16
+ * Live demo of `bf-progress`: a bar stepped by −10 % / +10 % buttons, a success bar and a `md` warning-tone bar.
17
+ */
18
+ @Component({
19
+ selector: 'app-example-progress-overview',
20
+ imports: [BfProgressComponent, BfButtonComponent],
21
+ template: `
22
+ <div class="flex flex-col gap-3">
23
+ <bf-progress [value]="value()" label="Atlas rebrand progress" />
24
+ <div class="flex items-center gap-2">
25
+ <bf-button variant="tonal" size="sm" [disabled]="value() <= 0" (clicked)="step(-10)">
26
+ −10 %
27
+ </bf-button>
28
+ <bf-button variant="tonal" size="sm" [disabled]="value() >= 100" (clicked)="step(10)">
29
+ +10 %
30
+ </bf-button>
31
+ <span data-status aria-live="polite" class="text-caption text-text-muted"
32
+ >{{ value() }} %</span
33
+ >
34
+ </div>
35
+ <bf-progress [value]="42" label="Bloom moodboard progress" tone="success" />
36
+ <bf-progress [value]="20" label="Storage used" size="md" tone="warning" />
37
+ </div>
38
+ `,
39
+ })
40
+ export class ProgressOverviewExampleComponent {
41
+ readonly value = signal(68);
42
+
43
+ step(delta: number): void {
44
+ this.value.update((value) => Math.min(100, Math.max(0, value + delta)));
45
+ }
46
+ }
47
+ ```
48
+
49
+ ## Overview
50
+
51
+ Progress bar for completion and quotas, determinate by default, or `indeterminate` while the
52
+ amount of work is unknown.
53
+
54
+ A progress bar for completion ("12 of 18 tasks") and quotas, or, with `indeterminate`, for work whose amount is not known yet.
55
+
56
+ ## When to use
57
+
58
+ Use `bf-progress` when the share of work done is known: tasks completed, storage used, an upload; use `indeterminate` for a bar-shaped wait whose amount is not known yet (an upload before its size arrives). Use [bf-spinner](https://bitforge-ui.web.app/components/spinner) instead for a short wait of unknown length inside a control, and [bf-badge](https://bitforge-ui.web.app/components/badge) for a plain status without a quantity.
59
+
60
+ ## Basic usage
61
+
62
+ `value` is the completion in percent, clamped to 0–100. Without `value` (and without `indeterminate`) the bar shows 0 %.
63
+
64
+ ```ts
65
+ import { Component } from '@angular/core';
66
+ import { BfProgressComponent } from '@bitforgehq/ui';
67
+
68
+ /** A labelled progress bar at 68 %. */
69
+ @Component({
70
+ selector: 'app-example-progress-basic',
71
+ imports: [BfProgressComponent],
72
+ template: `<bf-progress [value]="68" label="Atlas rebrand" />`,
73
+ })
74
+ export class ProgressBasicExampleComponent {}
75
+ ```
76
+
77
+ ## Value in words
78
+
79
+ `valueText` gives assistive tech the value in words (`aria-valuetext`), such as "12 of 18 tasks" instead of "67 %". Show the same words next to the bar for everyone else.
80
+
81
+ ```ts
82
+ import { Component } from '@angular/core';
83
+ import { BfProgressComponent } from '@bitforgehq/ui';
84
+
85
+ /**
86
+ * A progress bar that announces its value in words.
87
+ *
88
+ * `valueText` becomes `aria-valuetext`: "12 of 18 tasks" instead of "67 %".
89
+ */
90
+ @Component({
91
+ selector: 'app-example-progress-value-text',
92
+ imports: [BfProgressComponent],
93
+ template: `
94
+ <div class="flex flex-col gap-1.5">
95
+ <bf-progress [value]="67" label="Atlas rebrand" valueText="12 of 18 tasks" />
96
+ <span class="text-caption text-text-muted">12 of 18 tasks</span>
97
+ </div>
98
+ `,
99
+ })
100
+ export class ProgressValueTextExampleComponent {}
101
+ ```
102
+
103
+ ## Indeterminate
104
+
105
+ `indeterminate` drops the native value, so assistive tech announces a busy bar without a percentage, and a 40 % stripe runs across the track in the tone's colour. Under reduced motion (`prefers-reduced-motion: reduce`) the stripe stands still at the start: a static 40 % bar. `value` is ignored while `indeterminate` is set; set it back to `false` with the real value once it is known.
106
+
107
+ ```ts
108
+ import { Component, signal } from '@angular/core';
109
+ import { BfButtonComponent, BfProgressComponent } from '@bitforgehq/ui';
110
+
111
+ /**
112
+ * An indeterminate bar that turns determinate.
113
+ *
114
+ * While the size of the upload is unknown the bar is `indeterminate`; "Size known" sets the real
115
+ * value.
116
+ */
117
+ @Component({
118
+ selector: 'app-example-progress-indeterminate',
119
+ imports: [BfButtonComponent, BfProgressComponent],
120
+ template: `
121
+ <div class="flex flex-col gap-3">
122
+ <bf-progress label="Uploading report.pdf" [indeterminate]="unknown()" [value]="40" />
123
+ <bf-button variant="outline" size="sm" class="self-start" (clicked)="unknown.set(!unknown())">
124
+ {{ unknown() ? 'Size known' : 'Size unknown' }}
125
+ </bf-button>
126
+ </div>
127
+ `,
128
+ })
129
+ export class ProgressIndeterminateExampleComponent {
130
+ protected readonly unknown = signal(true);
131
+ }
132
+ ```
133
+
134
+ ## Size and tone
135
+
136
+ `md` (10 px) suits cards on mobile; the tone follows the meaning of the number.
137
+
138
+ ```ts
139
+ import { Component } from '@angular/core';
140
+ import { BfProgressComponent } from '@bitforgehq/ui';
141
+
142
+ /** A thicker progress bar in the warning tone. */
143
+ @Component({
144
+ selector: 'app-example-progress-size-tone',
145
+ imports: [BfProgressComponent],
146
+ template: `<bf-progress [value]="41" label="Bloom app" size="md" tone="warning" />`,
147
+ })
148
+ export class ProgressSizeToneExampleComponent {}
149
+ ```
150
+
151
+ ## Theming
152
+
153
+ - Track: `surface-sunken`.
154
+ - Fill by `tone`: `primary-strong` (the `primary` tone, default; ≥ 3:1 on the track for any
155
+ configured primary), `success`, `warning`, `danger`. The indeterminate stripe uses the same fill
156
+ (`CanvasText` under forced colours).
157
+ - Motion: the stripe runs with `--animate-progress-indeterminate` (1.4 s, `--ease-md`), and stops
158
+ under reduced motion.
159
+
160
+ ## Accessibility
161
+
162
+ Native `<progress>`, which is a `progressbar` without any role attribute, with `aria-label` carrying the required `label`; its `value`/`max` communicate the percentage to assistive tech, and `valueText` replaces it with words (`aria-valuetext`). An `indeterminate` bar renders no `value` attribute, the native indeterminate state; the stripe is `aria-hidden`.
163
+
164
+ ## Inputs
165
+
166
+ | Name | Type | Default | Description |
167
+ | --------------- | -------------------------------------------------------------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
168
+ | `value` | `number \| undefined` | `undefined` | Completion in percent; clamped to 0–100. Without it (and without `indeterminate`) the bar shows 0 %. Ignored while `indeterminate`. |
169
+ | `indeterminate` | `boolean` | `false` | The amount of work is unknown: the native bar carries no value (assistive tech announces it as busy) and a stripe runs across the track, a static 40 % bar under reduced motion. |
170
+ | `valueText` | `string` | `''` | The value in words for assistive tech (`aria-valuetext`), such as "12 of 18 tasks"; the percentage is announced while it is empty. |
171
+ | `label` | `string` | required | Accessible name of the bar ("Atlas rebrand"). |
172
+ | `size` | `BfProgressSize` (`'sm' \| 'md'`) | `'sm'` | Bar height: sm 8 px (default), md 10 px. |
173
+ | `tone` | `BfProgressTone` (`'primary' \| 'success' \| 'warning' \| 'danger'`) | `'primary'` | Fill color by meaning. |
174
+
175
+ ## Notes
176
+
177
+ ### Do
178
+
179
+ - Always pass `label`; it is what screen readers announce with the percentage.
180
+ - Put the numbers next to it in text ("12 of 18 tasks").
181
+
182
+ ### Don't
183
+
184
+ - Don't animate the bar on first paint from 0; set the real value immediately.
185
+
186
+ ## Migration notes
187
+
188
+ ### 0.3.0
189
+
190
+ **Behaviour:** the default `primary` tone fills with `primary-strong` (was `primary`), so the fill
191
+ reaches 3:1 on its `surface-sunken` track for any configured primary.
192
+
193
+ JSDoc of `progress.component.ts` for the API reference: `@summary`/`@since` on the exported symbols.
194
+ No behaviour change.
195
+
196
+ **Behaviour:** `value` is optional (`input<number | undefined>(undefined)`, was required); without
197
+ it and without `indeterminate` the bar renders 0 %. The redundant `role="progressbar"` attribute on
198
+ the native `<progress>` is dropped (it is a progressbar natively). New `valueText = input('')`,
199
+ rendered as `aria-valuetext` when set (F-039). New `indeterminate` (boolean): the native bar
200
+ renders without `value` (indeterminate for assistive tech) and a 40 % stripe in the tone's fill
201
+ runs across the track (`animate-progress-indeterminate`), standing still at the start under
202
+ `motion-reduce:`. The host is a one-cell `grid` (was `block`) with `overflow-hidden
203
+ rounded-full`, so the stripe shares the track's cell and stays in it.
204
+
205
+ The indeterminate stripe runs from the inline start: under `dir="rtl"` it takes
206
+ `--animate-progress-indeterminate-rtl` (mirrored keyframes), and stands still under reduced motion
207
+ in both directions.
208
+
209
+ ### 0.2.0
210
+
211
+ The value's width transition is off under `prefers-reduced-motion`.
212
+
213
+ ### 0.1.0
214
+
215
+ New `bf-progress`: a determinate `role="progressbar"` for completion and quotas. `value` is clamped to 0–100 and rounded; `size` is `sm` (8 px, default) or `md` (10 px); `tone` (`primary` default, `success`, `warning`, `danger`) colors the fill. `label` is required and becomes the bar's `aria-label`. The native `<progress>` element now carries an explicit `role="progressbar"` attribute (it already had the role implicitly) so consumers and specs can select on it directly. `percent` now guards against a non-finite `value` (e.g. `NaN`), rendering `0` instead of propagating `NaN` into the fill width and `aria-valuenow`.