@bitforgehq/ui 0.3.0 → 0.3.1

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 (144) hide show
  1. package/CHANGELOG.md +482 -0
  2. package/README.md +17 -11
  3. package/ai/MANIFEST.index.json +288 -0
  4. package/ai/MANIFEST.json +8716 -3926
  5. package/ai/REFERENCE.index.json +413 -0
  6. package/ai/REFERENCE.json +4710 -2047
  7. package/ai/UI_GUIDE.md +185 -57
  8. package/ai/components/back-button.md +42 -2
  9. package/ai/components/badge.md +17 -7
  10. package/ai/components/bar-chart.md +188 -0
  11. package/ai/components/breadcrumb-item.md +121 -0
  12. package/ai/components/breadcrumb.md +277 -0
  13. package/ai/components/button.md +82 -15
  14. package/ai/components/calendar.md +300 -0
  15. package/ai/components/card.md +63 -2
  16. package/ai/components/chart-card.md +283 -0
  17. package/ai/components/combobox.md +411 -0
  18. package/ai/components/date-input.md +396 -0
  19. package/ai/components/datetime-input.md +215 -0
  20. package/ai/components/description-item.md +111 -0
  21. package/ai/components/description-list.md +160 -0
  22. package/ai/components/donut-chart.md +169 -0
  23. package/ai/components/header.md +67 -2
  24. package/ai/components/icon-button.md +74 -16
  25. package/ai/components/icon.md +63 -7
  26. package/ai/components/line-chart.md +234 -0
  27. package/ai/components/list.md +70 -6
  28. package/ai/components/menu.md +5 -0
  29. package/ai/components/multi-combobox.md +257 -0
  30. package/ai/components/nav-group.md +178 -0
  31. package/ai/components/nav-item.md +210 -0
  32. package/ai/components/page-header.md +237 -0
  33. package/ai/components/pagination.md +173 -0
  34. package/ai/components/popover.md +250 -0
  35. package/ai/components/side-panel.md +391 -0
  36. package/ai/components/sidebar.md +363 -0
  37. package/ai/components/skeleton.md +215 -0
  38. package/ai/components/sparkline.md +188 -0
  39. package/ai/components/stat.md +233 -0
  40. package/ai/components/tab-group.md +226 -0
  41. package/ai/components/tab-panel.md +163 -0
  42. package/ai/components/table.md +568 -0
  43. package/ai/components/tabs.md +42 -2
  44. package/ai/components/theme-menu-button.md +182 -0
  45. package/ai/components/theme-switch.md +170 -0
  46. package/ai/components/time-input.md +240 -0
  47. package/ai/components/toolbar.md +289 -0
  48. package/ai/components/tooltip.md +230 -0
  49. package/ai/components/top-nav.md +159 -0
  50. package/ai/reference/bf-badge-appearance.md +36 -0
  51. package/ai/reference/bf-badge-variant.md +1 -1
  52. package/ai/reference/bf-bar-chart-orientation.md +27 -0
  53. package/ai/reference/bf-button-appearance.md +36 -0
  54. package/ai/reference/bf-button-variant.md +1 -1
  55. package/ai/reference/bf-calendar-disabled-date.md +23 -0
  56. package/ai/reference/bf-calendar-harness.md +51 -0
  57. package/ai/reference/bf-calendar-weekday.md +31 -0
  58. package/ai/reference/bf-chart-card-heading-level.md +26 -0
  59. package/ai/reference/bf-chart-curve.md +28 -0
  60. package/ai/reference/bf-chart-harness.md +51 -0
  61. package/ai/reference/bf-chart-legend-position.md +30 -0
  62. package/ai/reference/bf-chart-series.md +23 -0
  63. package/ai/reference/bf-chart-value-format.md +23 -0
  64. package/ai/reference/bf-color-scheme-service.md +30 -0
  65. package/ai/reference/bf-color-scheme.md +31 -0
  66. package/ai/reference/bf-combobox-filter.md +27 -0
  67. package/ai/reference/bf-combobox-harness.md +89 -0
  68. package/ai/reference/bf-combobox-option.md +33 -0
  69. package/ai/reference/bf-compare-with.md +2 -0
  70. package/ai/reference/bf-date-input-harness.md +67 -0
  71. package/ai/reference/bf-date-input-picker.md +27 -0
  72. package/ai/reference/bf-date-time-input-harness.md +76 -0
  73. package/ai/reference/bf-date-time-input-picker.md +27 -0
  74. package/ai/reference/bf-description-list-layout.md +26 -0
  75. package/ai/reference/bf-donut-slice.md +22 -0
  76. package/ai/reference/bf-labels.md +67 -20
  77. package/ai/reference/bf-menu-button-variant.md +1 -0
  78. package/ai/reference/bf-menu-placement.md +1 -0
  79. package/ai/reference/bf-overlay-options.md +28 -27
  80. package/ai/reference/bf-overlay-placement.md +34 -0
  81. package/ai/reference/bf-overlay-position.md +1 -1
  82. package/ai/reference/bf-overlay-service.md +5 -3
  83. package/ai/reference/bf-page-header-heading-level.md +26 -0
  84. package/ai/reference/bf-pagination-harness.md +61 -0
  85. package/ai/reference/bf-platform-service.md +17 -15
  86. package/ai/reference/bf-popover-harness.md +42 -0
  87. package/ai/reference/bf-popover-placement.md +28 -0
  88. package/ai/reference/bf-popover-trigger-directive.md +56 -0
  89. package/ai/reference/bf-side-panel-config.md +35 -0
  90. package/ai/reference/bf-side-panel-harness.md +34 -0
  91. package/ai/reference/bf-side-panel-ref.md +44 -0
  92. package/ai/reference/bf-side-panel-service.md +43 -0
  93. package/ai/reference/bf-side-panel-template-context.md +32 -0
  94. package/ai/reference/bf-side-panel-width.md +28 -0
  95. package/ai/reference/bf-size.md +12 -0
  96. package/ai/reference/bf-skeleton-shape.md +28 -0
  97. package/ai/reference/bf-stat-trend-intent.md +27 -0
  98. package/ai/reference/bf-stat-trend.md +27 -0
  99. package/ai/reference/bf-stat-variant.md +26 -0
  100. package/ai/reference/bf-tab-group-harness.md +44 -0
  101. package/ai/reference/bf-table-align.md +26 -0
  102. package/ai/reference/bf-table-cell-context.md +24 -0
  103. package/ai/reference/bf-table-cell-directive.md +22 -0
  104. package/ai/reference/bf-table-column.md +28 -0
  105. package/ai/reference/bf-table-harness.md +77 -0
  106. package/ai/reference/bf-table-row-key.md +21 -0
  107. package/ai/reference/bf-table-sort-direction.md +26 -0
  108. package/ai/reference/bf-table-sort.md +27 -0
  109. package/ai/reference/bf-theme.md +5 -4
  110. package/ai/reference/bf-time-input-harness.md +67 -0
  111. package/ai/reference/bf-time-input-hour-cycle.md +27 -0
  112. package/ai/reference/bf-time-input-picker.md +27 -0
  113. package/ai/reference/bf-tooltip-directive.md +43 -0
  114. package/ai/reference/bf-tooltip-harness.md +43 -0
  115. package/ai/reference/bf-tooltip-placement.md +28 -0
  116. package/ai/reference/bf_color_scheme_storage.md +20 -0
  117. package/ai/reference/bf_side_panel_data.md +17 -0
  118. package/ai/reference/bf_theme_storage.md +3 -1
  119. package/ai/reference/provide-bf-color-scheme.md +20 -0
  120. package/ai/reference/provide-bf-labels.md +12 -4
  121. package/ai/reference/slide-in-from-end.md +24 -0
  122. package/ai/reference/slide-out-to-end.md +24 -0
  123. package/fesm2022/bitforgehq-ui-charts.mjs +3045 -0
  124. package/fesm2022/bitforgehq-ui-charts.mjs.map +1 -0
  125. package/fesm2022/bitforgehq-ui-icons.mjs +107 -3
  126. package/fesm2022/bitforgehq-ui-icons.mjs.map +1 -1
  127. package/fesm2022/bitforgehq-ui-mobile.mjs +14 -59
  128. package/fesm2022/bitforgehq-ui-mobile.mjs.map +1 -1
  129. package/fesm2022/bitforgehq-ui-overlay.mjs +6436 -33
  130. package/fesm2022/bitforgehq-ui-overlay.mjs.map +1 -1
  131. package/fesm2022/bitforgehq-ui-testing.mjs +618 -1
  132. package/fesm2022/bitforgehq-ui-testing.mjs.map +1 -1
  133. package/fesm2022/bitforgehq-ui.mjs +5092 -153
  134. package/fesm2022/bitforgehq-ui.mjs.map +1 -1
  135. package/llms-full.txt +25177 -14398
  136. package/llms.txt +95 -1
  137. package/package.json +7 -3
  138. package/theme.css +101 -0
  139. package/types/bitforgehq-ui-charts.d.ts +967 -0
  140. package/types/bitforgehq-ui-icons.d.ts +13 -1
  141. package/types/bitforgehq-ui-mobile.d.ts +25 -18
  142. package/types/bitforgehq-ui-overlay.d.ts +1844 -7
  143. package/types/bitforgehq-ui-testing.d.ts +748 -1
  144. package/types/bitforgehq-ui.d.ts +2136 -44
@@ -0,0 +1,182 @@
1
+ # bf-theme-menu-button
2
+
3
+ Menu button with System, Light and Dark radio items bound to the colour scheme
4
+
5
+ `BfThemeMenuButtonComponent` from `@bitforgehq/ui/overlay` · Overlays · since 0.3.1
6
+
7
+ ## Example
8
+
9
+ ```ts
10
+ import { Component, computed, signal } from '@angular/core';
11
+ import { type BfColorScheme } from '@bitforgehq/ui';
12
+ import { BfIconComponent } from '@bitforgehq/ui/icons';
13
+ import { BfThemeMenuButtonComponent } from '@bitforgehq/ui/overlay';
14
+ import { Moon, Sun, SunMoon } from 'lucide';
15
+
16
+ /**
17
+ * The bf-theme-menu-button overview.
18
+ *
19
+ * Live demo of `bf-theme-menu-button`: an outline "Theme" button and an icon-only text button
20
+ * share one choice; the icon follows it (sun, moon, or both for System) and the status line
21
+ * reports it. The demo binds a local signal, so the docs site keeps its own theme; an app binds
22
+ * `BfColorSchemeService.scheme` instead.
23
+ */
24
+ @Component({
25
+ selector: 'app-example-theme-menu-button-overview',
26
+ imports: [BfIconComponent, BfThemeMenuButtonComponent],
27
+ template: `
28
+ <div class="flex flex-col gap-4">
29
+ <div class="flex flex-wrap items-center gap-2">
30
+ <bf-theme-menu-button placement="below-start" [(value)]="scheme">
31
+ <bf-icon bfSlot="icon" [icon]="icon()" />
32
+ </bf-theme-menu-button>
33
+ <bf-theme-menu-button iconOnly variant="text" [(value)]="scheme">
34
+ <bf-icon bfSlot="icon" [icon]="icon()" />
35
+ </bf-theme-menu-button>
36
+ </div>
37
+ <p data-status aria-live="polite" class="text-caption text-text-muted">
38
+ Theme: {{ scheme() }}
39
+ </p>
40
+ </div>
41
+ `,
42
+ })
43
+ export class ThemeMenuButtonOverviewExampleComponent {
44
+ /** Stands in for `BfColorSchemeService.scheme`. */
45
+ readonly scheme = signal<BfColorScheme>('system');
46
+ /** Sun for light, moon for dark, both while the choice follows the system. */
47
+ readonly icon = computed(() => ({ system: SunMoon, light: Sun, dark: Moon })[this.scheme()]);
48
+ }
49
+ ```
50
+
51
+ ## Overview
52
+
53
+ The colour scheme switch as a menu button, for a crowded top bar: a `bf-menu-button` whose menu
54
+ holds System, Light and Dark as radio items, the current one marked with a dot. It holds a
55
+ `BfColorScheme` and injects no service, so the app binds its own state, usually
56
+ `BfColorSchemeService.scheme` (`BfPlatformService.theme` in a Capacitor app). The button shows
57
+ `label` ("Theme") next to an optional icon, or only the icon with `iconOnly`.
58
+
59
+ A theme menu button lets the user pick the app's colour scheme from a menu, from `@bitforgehq/ui/overlay` (needs `@angular/cdk`, no Capacitor). It is a `bf-menu-button` whose `bf-menu` holds System, Light and Dark as radio items, the current one marked with a dot. It holds a `BfColorScheme` and injects no service: the app binds its own state, normally `BfColorSchemeService.scheme`, with `[(value)]`. It works in both shells. Import `BfThemeMenuButtonComponent` from `@bitforgehq/ui/overlay`.
60
+
61
+ ## When to use
62
+
63
+ Use `bf-theme-menu-button` in a crowded top bar or toolbar, where one button fits and three segments do not. Use [bf-theme-switch](https://bitforge-ui.web.app/components/theme-switch) instead on a settings page or in a header row with room, where the choice should stay visible. For a menu of other actions use [bf-menu-button](https://bitforge-ui.web.app/components/menu-button).
64
+
65
+ ## Icon only in a top bar
66
+
67
+ `iconOnly` renders a round button holding only the icon, like `bf-icon-button`; `label` (else the `theme` label, "Theme") is then its accessible name. Project the icon in the `icon` slot: the component never picks one. Show the current look, a sun or a moon from `BfColorSchemeService.dark()`, so under System the icon says what the OS chose. Bind `[(value)]` to `BfColorSchemeService.scheme`: on desktop with `provideBfColorScheme()` in the app's providers, in a Capacitor app through `provideBfShell()` (where `BfPlatformService.theme` is the same signal). `changed` emits the new choice after a user pick; picking the current item again changes nothing.
68
+
69
+ ```ts
70
+ import { Component, computed, inject } from '@angular/core';
71
+ import { BfColorSchemeService } from '@bitforgehq/ui';
72
+ import { BfIconComponent } from '@bitforgehq/ui/icons';
73
+ import { BfThemeMenuButtonComponent } from '@bitforgehq/ui/overlay';
74
+ import { Moon, Sun } from 'lucide';
75
+
76
+ /**
77
+ * An icon-only theme menu in a top bar, bound to the app's colour scheme.
78
+ *
79
+ * `[(value)]` binds `BfColorSchemeService.scheme`: with `provideBfColorScheme()` in the app's
80
+ * providers (desktop) or `provideBfShell()` (Capacitor), a pick sets the `dark` class on `<html>`
81
+ * and is stored. The icon follows `dark()`, so under System it shows what the OS chose.
82
+ *
83
+ * @preview none
84
+ */
85
+ @Component({
86
+ selector: 'app-example-theme-menu-button-top-bar',
87
+ imports: [BfIconComponent, BfThemeMenuButtonComponent],
88
+ template: `
89
+ <header class="border-border flex items-center gap-2 border-b px-4 py-2">
90
+ <span class="text-heading me-auto">Grove</span>
91
+ <bf-theme-menu-button iconOnly variant="text" [(value)]="colorScheme.scheme">
92
+ <bf-icon bfSlot="icon" [icon]="icon()" />
93
+ </bf-theme-menu-button>
94
+ </header>
95
+ `,
96
+ })
97
+ export class ThemeMenuButtonTopBarExampleComponent {
98
+ // app.config.ts: providers: [provideBfColorScheme()]
99
+ protected readonly colorScheme = inject(BfColorSchemeService);
100
+ protected readonly icon = computed(() => (this.colorScheme.dark() ? Moon : Sun));
101
+ }
102
+ ```
103
+
104
+ ## Label and variants
105
+
106
+ Without `iconOnly` the button shows `label` (else "Theme") after the optional icon, with the menu-button caret. It shows the name, not the current choice: the choice is the checked item in the menu (and the icon, when you project one), and a fixed text keeps the button's width and accessible name steady. `variant` is `outline` (default), `text` or `tonal`; `size` is `sm` 32, `md` 40 or `lg` 48 px, unset following `provideBfDefaults()`'s `controlSize`. `placement` defaults to `'below-end'`, since the button usually sits at the end of a bar.
107
+
108
+ ## Labels
109
+
110
+ The items read `themeSystem`, `themeLight` and `themeDark`; the button reads `theme`. Translate them with `provideBfLabels()`.
111
+
112
+ ## Testing
113
+
114
+ The component renders a `bf-menu-button`, so `BfMenuButtonHarness` from `@bitforgehq/ui/testing` drives it: filter it by `label: 'Theme'`, open it with `getMenu()`, then pick with `clickItem('Dark')`; `isChecked()` on a `BfMenuItemHarness` reads the dot.
115
+
116
+ ## Desktop
117
+
118
+ The button hovers, presses and shows the focus ring like `bf-menu-button` (and `bf-icon-button` with `iconOnly`): a darker fill or text on hover with the pointer cursor, the ring on keyboard focus only. A click opens the menu; menu rows take the muted fill on hover. Under a fine pointer the visible box is the hit area. In a dense top bar set `size="sm"`, or `provideBfDefaults({ size: 'sm' })` in a component's `providers` ([density guide](https://bitforge-ui.web.app/density)). Add `provideBfColorScheme()` to the desktop app's providers; the Capacitor shell needs nothing more than `provideBfShell()`.
119
+
120
+ ## Theming
121
+
122
+ The button consumes `bf-menu-button`'s tokens (`bf-button`'s, or `bf-icon-button`'s with `iconOnly`); the menu consumes `bf-menu`'s: `surface` with a `border` and `shadow-floating`, rows `text` with a `surface-muted` hover and focus, and the radio dot in the row's text colour.
123
+
124
+ ## Accessibility
125
+
126
+ - The button is a native `<button type="button">` with `aria-haspopup="menu"` and `aria-expanded`; its name is the visible label, or `label` (`aria-label`) with `iconOnly`. The menu it opens is named by it.
127
+ - The three items are `role="menuitemradio"` with `aria-checked`, so a screen reader announces the current choice. The dot is drawn in CSS and adds nothing to the name.
128
+ - Choosing an item closes the menu and returns focus to the button.
129
+ - Hit area: at least 32 px, 44 px under a coarse pointer, as `bf-menu-button`.
130
+
131
+ ### Keyboard
132
+
133
+ The keys inside the open menu are on the [bf-menu](https://bitforge-ui.web.app/components/menu#keyboard) page.
134
+
135
+ | Key | Action |
136
+ | ---------------------------------------------------------- | -------------------------------------------------------------- |
137
+ | <kbd>Enter</kbd> / <kbd>Space</kbd> / <kbd>ArrowDown</kbd> | Opens the menu and focuses System |
138
+ | <kbd>ArrowUp</kbd> | Opens the menu and focuses Dark |
139
+ | <kbd>Enter</kbd> / <kbd>Space</kbd> | In the open menu: picks the focused choice, closes the menu |
140
+ | <kbd>Escape</kbd> | In the open menu: closes it without a change and returns focus |
141
+
142
+ ## Inputs
143
+
144
+ | Name | Type | Default | Description |
145
+ | ----------- | ---------------------------------------------------------------------------------- | ------------- | ---------------------------------------------------------------------------------------------------------- |
146
+ | `label` | `string` | `''` | The button's text, and its accessible name with `iconOnly`; empty falls back to the `theme` label. |
147
+ | `iconOnly` | `boolean` | `false` | Square button holding only the projected icon, like `bf-icon-button`. |
148
+ | `variant` | `BfMenuButtonVariant` (`'outline' \| 'text' \| 'tonal'`) | `'outline'` | Visual style: outline (default), text, tonal. |
149
+ | `size` | `BfSize \| undefined` (`'sm' \| 'md' \| 'lg' \| undefined`) | `undefined` | Height step: sm 32, md 40, lg 48 px; unset follows `provideBfDefaults()` (`controlSize`, `md` by default). |
150
+ | `placement` | `BfMenuPlacement` (`'below-start' \| 'below-end' \| 'above-start' \| 'above-end'`) | `'below-end'` | Where the menu opens against the button; it flips when it would overflow. |
151
+ | `value` | `BfColorScheme` (`'system' \| 'light' \| 'dark'`) | `'system'` | The colour scheme choice; bind it two-way, usually to `BfColorSchemeService.scheme`. Two-way: `[(value)]`. |
152
+
153
+ ## Outputs
154
+
155
+ | Name | Payload | Description |
156
+ | --------- | ------------------------------------------------- | -------------------------------------------------------------------- |
157
+ | `changed` | `BfColorScheme` (`'system' \| 'light' \| 'dark'`) | Emitted with the new choice right after the user picks another item. |
158
+
159
+ ## Slots
160
+
161
+ - `icon`: The icon before the label, or the whole button with `iconOnly` (a sun or a moon).
162
+
163
+ ## Notes
164
+
165
+ ### Do
166
+
167
+ - Project a sun or moon icon that follows `BfColorSchemeService.dark()`.
168
+ - Bind `[(value)]` to `BfColorSchemeService.scheme` (or `BfPlatformService.theme` in the shell).
169
+
170
+ ### Don't
171
+
172
+ - Don't use it where there is room for three segments; use `bf-theme-switch`, which shows the choice without a click.
173
+ - Don't import it into a root-entry widget; it lives in `@bitforgehq/ui/overlay` because it opens a `bf-menu`.
174
+
175
+ ## Migration notes
176
+
177
+ ### 0.3.1
178
+
179
+ New `bf-theme-menu-button` in `@bitforgehq/ui/overlay`: a `bf-menu-button` whose menu holds
180
+ System, Light and Dark as radio items, for a crowded top bar. Same `value` / `changed` / `label`
181
+ as `bf-theme-switch`, plus `iconOnly`, `variant`, `size`, `placement` (default `'below-end'`) and
182
+ an `icon` slot for the consumer's sun or moon.
@@ -0,0 +1,170 @@
1
+ # bf-theme-switch
2
+
3
+ Segmented System, Light and Dark switch bound two-way to the app colour scheme
4
+
5
+ `BfThemeSwitchComponent` from `@bitforgehq/ui` · Forms · since 0.3.1
6
+
7
+ ## Example
8
+
9
+ ```ts
10
+ import { Component, signal } from '@angular/core';
11
+ import { BfThemeSwitchComponent, type BfColorScheme } from '@bitforgehq/ui';
12
+
13
+ /**
14
+ * The bf-theme-switch overview.
15
+ *
16
+ * Live demo of `bf-theme-switch`: the default switch and a `sm` one share one choice, and the
17
+ * status line reports it. The demo binds a local signal, so the docs site keeps its own theme; an
18
+ * app binds `BfColorSchemeService.scheme` instead.
19
+ */
20
+ @Component({
21
+ selector: 'app-example-theme-switch-overview',
22
+ imports: [BfThemeSwitchComponent],
23
+ template: `
24
+ <div class="flex flex-col items-start gap-3">
25
+ <bf-theme-switch [(value)]="scheme" />
26
+ <bf-theme-switch size="sm" label="Theme (small)" [(value)]="scheme" />
27
+ <p data-status aria-live="polite" class="text-caption text-text-muted">
28
+ Theme: {{ scheme() }}
29
+ </p>
30
+ </div>
31
+ `,
32
+ })
33
+ export class ThemeSwitchOverviewExampleComponent {
34
+ /** Stands in for `BfColorSchemeService.scheme`. */
35
+ readonly scheme = signal<BfColorScheme>('system');
36
+ }
37
+ ```
38
+
39
+ ## Overview
40
+
41
+ The colour scheme switch: System, Light and Dark as a three-option segmented control. It holds
42
+ a `BfColorScheme` and injects no service, so the app binds its own state, usually
43
+ `BfColorSchemeService.scheme` (`provideBfColorScheme()` on desktop, `provideBfShell()` in a
44
+ Capacitor app, where `BfPlatformService.theme` is the same signal).
45
+
46
+ A theme switch lets the user pick the app's colour scheme: System, Light or Dark, as a three-option `bf-segmented`. It holds a `BfColorScheme` and injects no service: the app binds its own state, normally `BfColorSchemeService.scheme`, with `[(value)]`. It works in both shells.
47
+
48
+ ## When to use
49
+
50
+ Use `bf-theme-switch` on a settings page, or in a header row with room for three segments, so the choice stays visible. Use [bf-theme-menu-button](https://bitforge-ui.web.app/components/theme-menu-button) instead in a crowded top bar, where only one button fits. For a general view switch use [bf-segmented](https://bitforge-ui.web.app/components/segmented) itself.
51
+
52
+ ## Bind the colour scheme
53
+
54
+ Bind `[(value)]` to `BfColorSchemeService.scheme` (from `@bitforgehq/ui`). On desktop, `provideBfColorScheme()` in the app's providers makes the service set the `dark` class on `<html>`, follow `prefers-color-scheme` under System and store the choice under `bf-theme`. In a Capacitor app `provideBfShell()` already does this, and `BfPlatformService.theme` (`@bitforgehq/ui/mobile`) is the same signal, so `[(value)]="platform.theme"` works too. `changed` emits the new choice after a user pick, never for a programmatic write.
55
+
56
+ ```ts
57
+ import { Component, inject } from '@angular/core';
58
+ import { BfColorSchemeService, BfThemeSwitchComponent } from '@bitforgehq/ui';
59
+
60
+ /**
61
+ * A theme switch bound to the app's colour scheme.
62
+ *
63
+ * `[(value)]` binds `BfColorSchemeService.scheme` itself: with `provideBfColorScheme()` in the
64
+ * app's providers (desktop) or `provideBfShell()` (Capacitor), a pick sets the `dark` class on
65
+ * `<html>` and is stored for the next visit.
66
+ *
67
+ * @preview none
68
+ */
69
+ @Component({
70
+ selector: 'app-example-theme-switch-color-scheme',
71
+ imports: [BfThemeSwitchComponent],
72
+ template: `<bf-theme-switch [(value)]="colorScheme.scheme" />`,
73
+ })
74
+ export class ThemeSwitchColorSchemeExampleComponent {
75
+ // app.config.ts: providers: [provideBfColorScheme()]
76
+ protected readonly colorScheme = inject(BfColorSchemeService);
77
+ }
78
+ ```
79
+
80
+ ## Full width
81
+
82
+ `fullWidth` stretches the switch and shares the width between the segments, as on a phone settings screen. `size` sets the height step (`sm` 32, `md` 40, `lg` 48 px; unset follows `provideBfDefaults()`'s `controlSize`).
83
+
84
+ ```ts
85
+ import { Component, signal } from '@angular/core';
86
+ import { BfThemeSwitchComponent, type BfColorScheme } from '@bitforgehq/ui';
87
+
88
+ /** A full-width theme switch under a visible caption, as on a phone settings screen. */
89
+ @Component({
90
+ selector: 'app-example-theme-switch-full-width',
91
+ imports: [BfThemeSwitchComponent],
92
+ template: `
93
+ <div class="flex max-w-sm flex-col gap-2">
94
+ <!-- The group's own name repeats the caption, so the caption is hidden from readers. -->
95
+ <p aria-hidden="true" class="text-caption text-text-muted">Appearance</p>
96
+ <bf-theme-switch fullWidth label="Appearance" [(value)]="scheme" />
97
+ </div>
98
+ `,
99
+ })
100
+ export class ThemeSwitchFullWidthExampleComponent {
101
+ protected readonly scheme = signal<BfColorScheme>('light');
102
+ }
103
+ ```
104
+
105
+ ## Labels
106
+
107
+ The segments read `themeSystem`, `themeLight` and `themeDark`, and the group's accessible name is `label`, else `theme` ("Theme"); translate them with `provideBfLabels()`. The name is not shown: put a visible caption beside it when the page needs one.
108
+
109
+ ## Testing
110
+
111
+ The switch renders a `bf-segmented`, so `BfSegmentedHarness` from `@bitforgehq/ui/testing` drives it: filter it by `label: 'Theme'`, pick with `select('Dark')` and read the choice with `getSelectedLabel()`.
112
+
113
+ ## Desktop
114
+
115
+ The segments hover, press and show the focus ring like `bf-segmented`'s: a darker label on hover with the pointer cursor, the ring on keyboard focus only. Under a fine pointer each segment is its own hit area. In a dense header row set `size="sm"`, or `provideBfDefaults({ size: 'sm' })` in a component's `providers` ([density guide](https://bitforge-ui.web.app/density)). Add `provideBfColorScheme()` to the desktop app's providers; the Capacitor shell needs nothing more than `provideBfShell()`.
116
+
117
+ ## Theming
118
+
119
+ The switch is a `bf-segmented` and consumes its tokens: track `surface-sunken`, active segment `surface` / `text` with `shadow-segment` and a 1.5 px inset `primary-strong` outline, inactive `text-muted`, focus `ring`, shape `rounded-control`.
120
+
121
+ ## Accessibility
122
+
123
+ A `bf-segmented`: `role="radiogroup"` named by `label` (else the `theme` label), each choice a `<button role="radio">` with `aria-checked`, one tab stop on the selected segment. A visible caption next to it should repeat the name or carry `aria-hidden="true"`, so readers do not hear it twice. Under a coarse pointer each segment's hit area reaches 44 px.
124
+
125
+ ### Keyboard
126
+
127
+ | Key | Action |
128
+ | ------------------------------------------------ | -------------------------------------------------------- |
129
+ | <kbd>Tab</kbd> / <kbd>Shift</kbd>+<kbd>Tab</kbd> | Moves focus into and out of the switch (one tab stop) |
130
+ | <kbd>ArrowRight</kbd> / <kbd>ArrowDown</kbd> | Selects and focuses the next choice, wrapping at the end |
131
+ | <kbd>ArrowLeft</kbd> / <kbd>ArrowUp</kbd> | Selects and focuses the previous choice, wrapping |
132
+ | <kbd>Home</kbd> / <kbd>End</kbd> | Selects System / Dark |
133
+ | <kbd>Space</kbd> / <kbd>Enter</kbd> | Selects the focused choice |
134
+
135
+ ## Inputs
136
+
137
+ | Name | Type | Default | Description |
138
+ | ----------- | ----------------------------------------------------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------- |
139
+ | `label` | `string` | `''` | Accessible name of the switch (not shown); empty falls back to the `theme` label. |
140
+ | `size` | `BfSize \| undefined` (`'sm' \| 'md' \| 'lg' \| undefined`) | `undefined` | Height step: sm 32, md 40, lg 48 px; unset follows `provideBfDefaults()` (`controlSize`, `md` by default). |
141
+ | `fullWidth` | `boolean` | `false` | Stretch the switch and share the width between the three segments. |
142
+ | `value` | `BfColorScheme` (`'system' \| 'light' \| 'dark'`) | `'system'` | The colour scheme choice; bind it two-way, usually to `BfColorSchemeService.scheme`, or with `[formField]`. Two-way: `[(value)]`. |
143
+
144
+ ## Outputs
145
+
146
+ | Name | Payload | Description |
147
+ | --------- | ------------------------------------------------- | ----------------------------------------------------------------------- |
148
+ | `changed` | `BfColorScheme` (`'system' \| 'light' \| 'dark'`) | Emitted with the new choice right after the user picks another segment. |
149
+
150
+ ## Notes
151
+
152
+ ### Do
153
+
154
+ - Bind `[(value)]` to `BfColorSchemeService.scheme` (or `BfPlatformService.theme` in the shell), so the switch and the page agree.
155
+ - Keep System as an option: it follows the OS until the user decides.
156
+
157
+ ### Don't
158
+
159
+ - Don't call `scheme.set()` from `(changed)` as well as binding `[(value)]`; the two-way binding already writes it.
160
+ - Don't squeeze the switch into a crowded top bar; use `bf-theme-menu-button` there.
161
+
162
+ ## Migration notes
163
+
164
+ ### 0.3.1
165
+
166
+ New `bf-theme-switch` in `@bitforgehq/ui`: System, Light and Dark as a three-option
167
+ `bf-segmented`, holding a `BfColorScheme` in `value` (`[(value)]`, `changed`), with `label`,
168
+ `size` and `fullWidth`. It injects no service; bind it to `BfColorSchemeService.scheme`
169
+ (`BfPlatformService.theme` in the shell). New `BfLabels` keys `theme`, `themeSystem`, `themeLight`
170
+ and `themeDark`.
@@ -0,0 +1,240 @@
1
+ # bf-time-input
2
+
3
+ Typed time-of-day field with a popover list of times, valued as an ISO string
4
+
5
+ `BfTimeInputComponent` from `@bitforgehq/ui/overlay` · Forms · since 0.3.1
6
+
7
+ ## Example
8
+
9
+ ```ts
10
+ import { Component, computed, signal } from '@angular/core';
11
+ import { BfTimeInputComponent } from '@bitforgehq/ui/overlay';
12
+
13
+ /**
14
+ * The bf-time-input overview.
15
+ *
16
+ * Live demo of `bf-time-input`: a start time typed as hour and minute (24-hour in British
17
+ * English) or picked from the list of times every 30 minutes, which opens scrolled to the value.
18
+ * The value is an ISO time string; the status line shows it.
19
+ */
20
+ @Component({
21
+ selector: 'app-example-time-input-overview',
22
+ imports: [BfTimeInputComponent],
23
+ template: `
24
+ <div class="flex max-w-xs flex-col gap-3">
25
+ <bf-time-input label="Start" locale="en-GB" [(value)]="start" />
26
+ <p data-status aria-live="polite" class="text-caption text-text-muted">{{ status() }}</p>
27
+ </div>
28
+ `,
29
+ })
30
+ export class TimeInputOverviewExampleComponent {
31
+ /** The start time, 'HH:mm'; null while empty or incomplete. */
32
+ readonly start = signal<string | null>('09:30');
33
+
34
+ readonly status = computed(() => `Start: ${this.start() ?? 'none'}`);
35
+ }
36
+ ```
37
+
38
+ ## Overview
39
+
40
+ Time field typed part by part (hour, minute, optional seconds, AM/PM under a 12-hour clock)
41
+ with a popover list of times, valued as an ISO time string.
42
+
43
+ A time-of-day field typed part by part: the hour and the minute (and the seconds with `seconds`) in the locale's order and clock, with an AM/PM part under a 12-hour clock. A clock button opens a list of times every `step` minutes in a popover, scrolled to the value. The value is an ISO time string (`'14:30'`, `'14:30:05'` with seconds) on a 24-hour clock whatever the field shows, with no date and no time zone.
44
+
45
+ Import `BfTimeInputComponent` from `@bitforgehq/ui/overlay` (it needs `@angular/cdk` for the popover). The parts work as in [bf-date-input](https://bitforge-ui.web.app/components/date-input); for a date and a time in one field use [bf-datetime-input](https://bitforge-ui.web.app/components/datetime-input).
46
+
47
+ ## When to use
48
+
49
+ Use `bf-time-input` for a time of day in a form: a start time, an alarm, opening hours. Use [bf-select](https://bitforge-ui.web.app/components/select) or [bf-segmented](https://bitforge-ui.web.app/components/segmented) instead for a handful of fixed slots (morning, afternoon), and [bf-number-input](https://bitforge-ui.web.app/components/number-input) for a duration ("90 minutes"), which is not a time of day. It works in both shells; in a Capacitor app set `native`, so a phone shows its own time picker (see Native picker).
50
+
51
+ ## Typing a time
52
+
53
+ Each part takes digits and moves on once it is complete ("7" in the hour of a 24-hour clock waits for a second digit, "3" moves on), and a part takes its value only then or when you leave it, so typing "30" minutes never passes through 03. <kbd>ArrowUp</kbd> and <kbd>ArrowDown</kbd> step a part and wrap (59 to 00), <kbd>Backspace</kbd> clears it, <kbd>ArrowLeft</kbd> and <kbd>ArrowRight</kbd> move between parts and <kbd>:</kbd> moves on. `value` is `null` while any part is empty, and `(changed)` emits the new value right after a user change.
54
+
55
+ ## 12-hour clock
56
+
57
+ The clock follows the locale (`Intl.DateTimeFormat`'s `hourCycle`): American English counts 1 to 12 with an AM/PM part, British English and German count 0 to 23. `hourCycle` forces `h12` or `h23`. The AM/PM part takes the first letter of the locale's AM or PM name, or the arrows; the names come from `Intl`, its spinbutton name from the `timeInputDayPeriod` label. The value stays a 24-hour time: 2:30 PM is `'14:30'`, 12:05 AM is `'00:05'`.
58
+
59
+ ```ts
60
+ import { Component, signal } from '@angular/core';
61
+ import { BfTimeInputComponent } from '@bitforgehq/ui/overlay';
62
+
63
+ /**
64
+ * A 12-hour clock.
65
+ *
66
+ * American English counts hours 1 to 12 with an AM/PM part (type A or P, or press the arrows);
67
+ * `hourCycle="h12"` forces it in any locale. The value stays a 24-hour ISO time: 2:30 PM is
68
+ * '14:30'.
69
+ */
70
+ @Component({
71
+ selector: 'app-example-time-input-twelve-hour',
72
+ imports: [BfTimeInputComponent],
73
+ template: `<bf-time-input class="max-w-xs" label="Alarm" locale="en-US" [(value)]="alarm" />`,
74
+ })
75
+ export class TimeInputTwelveHourExampleComponent {
76
+ readonly alarm = signal<string | null>('14:30');
77
+ }
78
+ ```
79
+
80
+ ## List of times
81
+
82
+ The clock button opens a list of times every `step` minutes from midnight (30 by default, any number from 1 to 720), written in the locale's clock. It opens scrolled to the value (else to the next time from now) with keyboard focus on it; <kbd>ArrowUp</kbd> and <kbd>ArrowDown</kbd> move, <kbd>Enter</kbd> or a click picks a time, closes the list and returns focus to the button. `min` and `max` disable the times outside them, and a typed time outside them shows `timeInputMin` / `timeInputMax` once focus leaves the field; an incomplete time shows `timeInputInvalid`. `picker="none"` leaves the field typed only.
83
+
84
+ ```ts
85
+ import { Component, signal } from '@angular/core';
86
+ import { BfTimeInputComponent } from '@bitforgehq/ui/overlay';
87
+
88
+ /**
89
+ * Step, min and max.
90
+ *
91
+ * `step="15"` lists a time every 15 minutes, and `min` and `max` disable the times outside
92
+ * opening hours; a typed time outside them shows an error once focus leaves the field.
93
+ */
94
+ @Component({
95
+ selector: 'app-example-time-input-step',
96
+ imports: [BfTimeInputComponent],
97
+ template: `
98
+ <bf-time-input
99
+ class="max-w-xs"
100
+ label="Pickup"
101
+ locale="en-GB"
102
+ helpText="Between 08:00 and 18:00."
103
+ step="15"
104
+ min="08:00"
105
+ max="18:00"
106
+ [(value)]="pickup"
107
+ />
108
+ `,
109
+ })
110
+ export class TimeInputStepExampleComponent {
111
+ readonly pickup = signal<string | null>(null);
112
+ }
113
+ ```
114
+
115
+ ## Seconds
116
+
117
+ `seconds` adds a seconds part, and the value becomes `'HH:mm:ss'`; the list's times then end in `:00`. Leave it off unless the seconds matter (a log filter, a timer).
118
+
119
+ ## Native picker
120
+
121
+ `native` renders an `input type="time"` with the same label, help and error text when the primary pointer is coarse (`matchMedia('(pointer: coarse)')`), and the segmented field under a mouse. `min`, `max` and `seconds` go to the native input. Recommended in the Capacitor shell: phones show their own time wheel, and typing into the parts needs a hardware keyboard.
122
+
123
+ ## With Signal Forms
124
+
125
+ Bind the field with `[formField]`: required, touched, errors, disabled and readonly flow in, the first error shows once focus has left the field, and `focus()` focuses the first empty part. The [bf-date-input](https://bitforge-ui.web.app/components/date-input) page shows a booking form with both fields. Under `[formField]` the form owns `min` and `max`: give them through the schema with a metadata key (`metadata(path.time, OPENS, () => '09:00')` and `metadata(path.time, MIN, () => OPENS)`, with `OPENS = createMetadataKey<string | undefined>()`). In specs, `BfTimeInputHarness` from `@bitforgehq/ui/testing` types a time (`setValue('2:30 PM')`) or picks one from the list (`pickTime('14:30')`).
126
+
127
+ ## Desktop
128
+
129
+ The field shows the `primary` border and focus ring while any part has focus, the focused part tinted `primary-soft`. The list's times tint `surface-muted` on hover; the keyboard's active time carries the focus ring while the list has keyboard focus. Fields default to `md` (40 px); `size` and `provideBfDefaults({ fieldSize })` set the step as on `bf-input` ([density guide](https://bitforge-ui.web.app/density)).
130
+
131
+ ## Theming
132
+
133
+ - Field, parts and button: as `bf-date-input` (`surface`, `border-strong`, `primary` focus, `primary-soft` focused part, `text-muted` placeholders).
134
+ - List: times `text` with `rounded-control` pills, hover `surface-muted`; the selected time `primary` with `on-primary`, bold; disabled times `text-disabled`; the active time `ring`.
135
+ - Popover: `surface`, `border`, `rounded-card`, `shadow-floating`.
136
+
137
+ ## Accessibility
138
+
139
+ The parts are a `role="group"` named by `label` (else `ariaLabel` or `ariaLabelledby`); each part is a `role="spinbutton"` named from the `timeInputHour`, `timeInputMinute`, `timeInputSecond` and `timeInputDayPeriod` labels, with `aria-valuemin`, `aria-valuemax`, `aria-valuenow` and `aria-valuetext` (the AM or PM name), and the field's `aria-invalid`, `aria-errormessage`, `aria-required`, `aria-readonly` and `aria-disabled`. The clock button is named by `timeInputOpenList`, with `aria-haspopup="dialog"`, `aria-expanded` and `aria-controls`. The list is a `role="listbox"` named by the same label, its times `role="option"` with `aria-selected` and `aria-disabled`; focus stays on the list and `aria-activedescendant` names the active time.
140
+
141
+ ### Keyboard
142
+
143
+ | Key | Where | Action |
144
+ | ------------------------------------------------ | ----- | -------------------------------------------------------- |
145
+ | <kbd>Tab</kbd> / <kbd>Shift</kbd>+<kbd>Tab</kbd> | Field | Moves through the parts, then to the clock button |
146
+ | <kbd>0</kbd>–<kbd>9</kbd> | Part | Types into the part; moves on once no further digit fits |
147
+ | <kbd>A</kbd> / <kbd>P</kbd> | AM/PM | Sets the day period (the first letter of its name) |
148
+ | <kbd>ArrowUp</kbd> / <kbd>ArrowDown</kbd> | Part | Next / previous value, wrapping |
149
+ | <kbd>ArrowRight</kbd> / <kbd>ArrowLeft</kbd> | Part | Next / previous part (swapped under `dir="rtl"`) |
150
+ | <kbd>Backspace</kbd> / <kbd>Delete</kbd> | Part | Clears the part |
151
+ | <kbd>:</kbd> | Part | Moves on to the next part |
152
+ | <kbd>Alt</kbd>+<kbd>ArrowDown</kbd> | Part | Opens the list |
153
+ | <kbd>ArrowDown</kbd> / <kbd>ArrowUp</kbd> | List | Next / previous time (skipping disabled ones) |
154
+ | <kbd>Home</kbd> / <kbd>End</kbd> | List | First / last time |
155
+ | <kbd>Enter</kbd> / <kbd>Space</kbd> | List | Picks the active time and closes the list |
156
+ | <kbd>Escape</kbd> | List | Closes the list, focus back on the button |
157
+ | <kbd>Tab</kbd> | List | Closes the list and moves on |
158
+
159
+ ## Inputs
160
+
161
+ | Name | Type | Default | Description |
162
+ | ---------------- | ------------------------------------------------------------------- | ----------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
163
+ | `min` | `string \| undefined` | `undefined` | The earliest valid time ('HH:mm'): earlier times show an error and are disabled in the list. Set by `[formField]`; do not bind it yourself. |
164
+ | `max` | `string \| undefined` | `undefined` | The latest valid time ('HH:mm'): later times show an error and are disabled in the list. Set by `[formField]`; do not bind it yourself. |
165
+ | `step` | `number` | `30` | Minutes between the times of the list, from midnight (1–720). |
166
+ | `seconds` | `boolean` | `false` | Adds a seconds part; the value becomes 'HH:mm:ss'. |
167
+ | `hourCycle` | `BfTimeInputHourCycle \| undefined` (`'h12' \| 'h23' \| undefined`) | `undefined` | Forces a 12-hour (`h12`, with AM/PM) or 24-hour (`h23`) clock; unset follows the locale. |
168
+ | `picker` | `BfTimeInputPicker` (`'list' \| 'none'`) | `'list'` | `list` adds the button that opens the list of times; `none` keeps the field typed only. |
169
+ | `native` | `boolean` | `false` | Renders the platform's time picker (`input type="time"`) under a coarse pointer (touch). |
170
+ | `locale` | `string` | `''` | BCP 47 locale of the part order, the separators, the clock and the AM/PM names; empty follows the document's `lang`, else English. |
171
+ | `size` | `BfSize \| undefined` (`'sm' \| 'md' \| 'lg' \| undefined`) | `undefined` | Height step: sm 32, md 40, lg 48 px; unset follows `provideBfDefaults()` (`fieldSize`, `md` by default). |
172
+ | `label` | `string` | `''` | Visible label above the field. |
173
+ | `ariaLabel` | `string` | `''` | Accessible name when no visible label is rendered. Ignored when `label` is set. |
174
+ | `ariaLabelledby` | `string` | `''` | Id(s) of the element(s) that name the field when no visible label is rendered. Ignored when `label` is set. |
175
+ | `helpText` | `string` | `''` | Helper text under the field. |
176
+ | `errorMessage` | `string` | `''` | Explicit error text; takes precedence over the field's own errors and Signal Forms errors. |
177
+ | `disabled` | `boolean` | `false` | Disables the field and its list button. Set by `[formField]`; do not bind it yourself. |
178
+ | `readonly` | `boolean` | `false` | Shows the time but blocks changes; the parts stay focusable and expose `aria-readonly`. Set by `[formField]`; do not bind it yourself. |
179
+ | `required` | `boolean` | `false` | Marks the field required. Set by `[formField]`; do not bind it yourself. |
180
+ | `invalid` | `boolean` | `false` | Field validity from Signal Forms. Set by `[formField]`; do not bind it yourself. |
181
+ | `touched` | `boolean` | `false` | Whether the user has left the field. Set by `[formField]`; do not bind it yourself. |
182
+ | `errors` | `readonly ValidationError.WithOptionalFieldTree[]` | `[]` | Validation errors from Signal Forms. Set by `[formField]`; do not bind it yourself. |
183
+ | `name` | `string` | `''` | Native name attribute of the platform input (`native` under a coarse pointer). Set by `[formField]`; do not bind it yourself. |
184
+ | `id` | `string` | `generated` | Field id; generated when not set. |
185
+ | `value` | `string \| null` | `null` | The time as an ISO string ('HH:mm', 'HH:mm:ss' with `seconds`), `null` while empty or incomplete; two-way bindable and bound by `[formField]`. Two-way: `[(value)]`. |
186
+
187
+ ## Outputs
188
+
189
+ | Name | Payload | Description |
190
+ | --------- | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------- |
191
+ | `changed` | `string \| null` | Emitted with the new time right after a user change: `null` once it is cleared or incomplete. |
192
+ | `touch` | `void` | Emitted when focus leaves the field (not into its list), so Signal Forms marks it touched. Set by `[formField]`; do not bind it yourself. |
193
+
194
+ ## Methods
195
+
196
+ ### `focus(options?: FocusOptions): void`
197
+
198
+ Focuses the first empty part (else the first part); Signal Forms calls it from
199
+ `focusBoundControl()`.
200
+
201
+ | Parameter | Type | Description |
202
+ | ---------- | -------------- | --------------------------------------------------------------------- |
203
+ | `options?` | `FocusOptions` | Passed on to the native `focus()`, such as `{ preventScroll: true }`. |
204
+
205
+ ### `openPicker(): void`
206
+
207
+ Opens the list of times; does nothing without a picker, or while disabled or read-only.
208
+
209
+ ### `closePicker(): void`
210
+
211
+ Closes the list of times, focus back on its button; does nothing while it is closed.
212
+
213
+ ## Notes
214
+
215
+ ### Do
216
+
217
+ - Give the field a `label`.
218
+ - Store the value as the ISO time it is, and combine it with a date only where you know the time zone.
219
+ - Pick a `step` that matches the slots you accept (15 for appointments).
220
+
221
+ ### Don't
222
+
223
+ - Don't use it for a duration; a time input is a time of day.
224
+ - Don't add `seconds` for times people pick by the quarter hour.
225
+ - Don't format the value yourself for display in the field; it follows the locale's clock.
226
+
227
+ ## Migration notes
228
+
229
+ ### 0.3.1
230
+
231
+ New `bf-time-input` in `@bitforgehq/ui/overlay`: a time-of-day field typed as hour and minute
232
+ (and second with `seconds`), with an AM/PM part under a 12-hour clock. The clock follows the
233
+ locale; `hourCycle` (`BfTimeInputHourCycle`: `'h12'` or `'h23'`) forces one. The value is an ISO
234
+ time on a 24-hour clock (`'HH:mm'`, `'HH:mm:ss'` with seconds). `picker` (`BfTimeInputPicker`:
235
+ `'list'`, the default, or `'none'`) adds a button that opens a listbox of times every `step`
236
+ minutes (30 by default), scrolled to the value; `min` and `max` disable the times outside them
237
+ and show an error for a typed one. `native` renders `input type="time"` under a coarse pointer.
238
+ New label keys `timeInputHour`, `timeInputMinute`, `timeInputSecond`, `timeInputDayPeriod`,
239
+ `timeInputOpenList`, `timeInputInvalid`, `timeInputMin` and `timeInputMax`, and a
240
+ `BfTimeInputHarness` in `@bitforgehq/ui/testing` (`setValue(text)`, `pickTime(time)`).