@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.
- package/CHANGELOG.md +482 -0
- package/README.md +17 -11
- package/ai/MANIFEST.index.json +288 -0
- package/ai/MANIFEST.json +8716 -3926
- package/ai/REFERENCE.index.json +413 -0
- package/ai/REFERENCE.json +4710 -2047
- package/ai/UI_GUIDE.md +185 -57
- package/ai/components/back-button.md +42 -2
- package/ai/components/badge.md +17 -7
- package/ai/components/bar-chart.md +188 -0
- package/ai/components/breadcrumb-item.md +121 -0
- package/ai/components/breadcrumb.md +277 -0
- package/ai/components/button.md +82 -15
- package/ai/components/calendar.md +300 -0
- package/ai/components/card.md +63 -2
- package/ai/components/chart-card.md +283 -0
- package/ai/components/combobox.md +411 -0
- package/ai/components/date-input.md +396 -0
- package/ai/components/datetime-input.md +215 -0
- package/ai/components/description-item.md +111 -0
- package/ai/components/description-list.md +160 -0
- package/ai/components/donut-chart.md +169 -0
- package/ai/components/header.md +67 -2
- package/ai/components/icon-button.md +74 -16
- package/ai/components/icon.md +63 -7
- package/ai/components/line-chart.md +234 -0
- package/ai/components/list.md +70 -6
- package/ai/components/menu.md +5 -0
- package/ai/components/multi-combobox.md +257 -0
- package/ai/components/nav-group.md +178 -0
- package/ai/components/nav-item.md +210 -0
- package/ai/components/page-header.md +237 -0
- package/ai/components/pagination.md +173 -0
- package/ai/components/popover.md +250 -0
- package/ai/components/side-panel.md +391 -0
- package/ai/components/sidebar.md +363 -0
- package/ai/components/skeleton.md +215 -0
- package/ai/components/sparkline.md +188 -0
- package/ai/components/stat.md +233 -0
- package/ai/components/tab-group.md +226 -0
- package/ai/components/tab-panel.md +163 -0
- package/ai/components/table.md +568 -0
- package/ai/components/tabs.md +42 -2
- package/ai/components/theme-menu-button.md +182 -0
- package/ai/components/theme-switch.md +170 -0
- package/ai/components/time-input.md +240 -0
- package/ai/components/toolbar.md +289 -0
- package/ai/components/tooltip.md +230 -0
- package/ai/components/top-nav.md +159 -0
- package/ai/reference/bf-badge-appearance.md +36 -0
- package/ai/reference/bf-badge-variant.md +1 -1
- package/ai/reference/bf-bar-chart-orientation.md +27 -0
- package/ai/reference/bf-button-appearance.md +36 -0
- package/ai/reference/bf-button-variant.md +1 -1
- package/ai/reference/bf-calendar-disabled-date.md +23 -0
- package/ai/reference/bf-calendar-harness.md +51 -0
- package/ai/reference/bf-calendar-weekday.md +31 -0
- package/ai/reference/bf-chart-card-heading-level.md +26 -0
- package/ai/reference/bf-chart-curve.md +28 -0
- package/ai/reference/bf-chart-harness.md +51 -0
- package/ai/reference/bf-chart-legend-position.md +30 -0
- package/ai/reference/bf-chart-series.md +23 -0
- package/ai/reference/bf-chart-value-format.md +23 -0
- package/ai/reference/bf-color-scheme-service.md +30 -0
- package/ai/reference/bf-color-scheme.md +31 -0
- package/ai/reference/bf-combobox-filter.md +27 -0
- package/ai/reference/bf-combobox-harness.md +89 -0
- package/ai/reference/bf-combobox-option.md +33 -0
- package/ai/reference/bf-compare-with.md +2 -0
- package/ai/reference/bf-date-input-harness.md +67 -0
- package/ai/reference/bf-date-input-picker.md +27 -0
- package/ai/reference/bf-date-time-input-harness.md +76 -0
- package/ai/reference/bf-date-time-input-picker.md +27 -0
- package/ai/reference/bf-description-list-layout.md +26 -0
- package/ai/reference/bf-donut-slice.md +22 -0
- package/ai/reference/bf-labels.md +67 -20
- package/ai/reference/bf-menu-button-variant.md +1 -0
- package/ai/reference/bf-menu-placement.md +1 -0
- package/ai/reference/bf-overlay-options.md +28 -27
- package/ai/reference/bf-overlay-placement.md +34 -0
- package/ai/reference/bf-overlay-position.md +1 -1
- package/ai/reference/bf-overlay-service.md +5 -3
- package/ai/reference/bf-page-header-heading-level.md +26 -0
- package/ai/reference/bf-pagination-harness.md +61 -0
- package/ai/reference/bf-platform-service.md +17 -15
- package/ai/reference/bf-popover-harness.md +42 -0
- package/ai/reference/bf-popover-placement.md +28 -0
- package/ai/reference/bf-popover-trigger-directive.md +56 -0
- package/ai/reference/bf-side-panel-config.md +35 -0
- package/ai/reference/bf-side-panel-harness.md +34 -0
- package/ai/reference/bf-side-panel-ref.md +44 -0
- package/ai/reference/bf-side-panel-service.md +43 -0
- package/ai/reference/bf-side-panel-template-context.md +32 -0
- package/ai/reference/bf-side-panel-width.md +28 -0
- package/ai/reference/bf-size.md +12 -0
- package/ai/reference/bf-skeleton-shape.md +28 -0
- package/ai/reference/bf-stat-trend-intent.md +27 -0
- package/ai/reference/bf-stat-trend.md +27 -0
- package/ai/reference/bf-stat-variant.md +26 -0
- package/ai/reference/bf-tab-group-harness.md +44 -0
- package/ai/reference/bf-table-align.md +26 -0
- package/ai/reference/bf-table-cell-context.md +24 -0
- package/ai/reference/bf-table-cell-directive.md +22 -0
- package/ai/reference/bf-table-column.md +28 -0
- package/ai/reference/bf-table-harness.md +77 -0
- package/ai/reference/bf-table-row-key.md +21 -0
- package/ai/reference/bf-table-sort-direction.md +26 -0
- package/ai/reference/bf-table-sort.md +27 -0
- package/ai/reference/bf-theme.md +5 -4
- package/ai/reference/bf-time-input-harness.md +67 -0
- package/ai/reference/bf-time-input-hour-cycle.md +27 -0
- package/ai/reference/bf-time-input-picker.md +27 -0
- package/ai/reference/bf-tooltip-directive.md +43 -0
- package/ai/reference/bf-tooltip-harness.md +43 -0
- package/ai/reference/bf-tooltip-placement.md +28 -0
- package/ai/reference/bf_color_scheme_storage.md +20 -0
- package/ai/reference/bf_side_panel_data.md +17 -0
- package/ai/reference/bf_theme_storage.md +3 -1
- package/ai/reference/provide-bf-color-scheme.md +20 -0
- package/ai/reference/provide-bf-labels.md +12 -4
- package/ai/reference/slide-in-from-end.md +24 -0
- package/ai/reference/slide-out-to-end.md +24 -0
- package/fesm2022/bitforgehq-ui-charts.mjs +3045 -0
- package/fesm2022/bitforgehq-ui-charts.mjs.map +1 -0
- package/fesm2022/bitforgehq-ui-icons.mjs +107 -3
- package/fesm2022/bitforgehq-ui-icons.mjs.map +1 -1
- package/fesm2022/bitforgehq-ui-mobile.mjs +14 -59
- package/fesm2022/bitforgehq-ui-mobile.mjs.map +1 -1
- package/fesm2022/bitforgehq-ui-overlay.mjs +6436 -33
- package/fesm2022/bitforgehq-ui-overlay.mjs.map +1 -1
- package/fesm2022/bitforgehq-ui-testing.mjs +618 -1
- package/fesm2022/bitforgehq-ui-testing.mjs.map +1 -1
- package/fesm2022/bitforgehq-ui.mjs +5092 -153
- package/fesm2022/bitforgehq-ui.mjs.map +1 -1
- package/llms-full.txt +25177 -14398
- package/llms.txt +95 -1
- package/package.json +7 -3
- package/theme.css +101 -0
- package/types/bitforgehq-ui-charts.d.ts +967 -0
- package/types/bitforgehq-ui-icons.d.ts +13 -1
- package/types/bitforgehq-ui-mobile.d.ts +25 -18
- package/types/bitforgehq-ui-overlay.d.ts +1844 -7
- package/types/bitforgehq-ui-testing.d.ts +748 -1
- 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)`).
|