@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,188 @@
|
|
|
1
|
+
# bf-sparkline
|
|
2
|
+
|
|
3
|
+
Tiny trend line with no axes or legend, for stats and table cells
|
|
4
|
+
|
|
5
|
+
`BfSparklineComponent` from `@bitforgehq/ui/charts` · Data display · since 0.3.1
|
|
6
|
+
|
|
7
|
+
## Example
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { Component } from '@angular/core';
|
|
11
|
+
import { BfStatComponent } from '@bitforgehq/ui';
|
|
12
|
+
import { BfSparklineComponent } from '@bitforgehq/ui/charts';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The bf-sparkline overview.
|
|
16
|
+
*
|
|
17
|
+
* Live demo of `bf-sparkline`: three stats with a 30-day trend in their supporting line, the
|
|
18
|
+
* trend also stated in words; the middle one fills its area.
|
|
19
|
+
*/
|
|
20
|
+
@Component({
|
|
21
|
+
selector: 'app-example-sparkline-overview',
|
|
22
|
+
imports: [BfStatComponent, BfSparklineComponent],
|
|
23
|
+
template: `
|
|
24
|
+
<div class="grid w-full grid-cols-[repeat(auto-fit,minmax(10rem,1fr))] gap-3">
|
|
25
|
+
<bf-stat label="Revenue" value="CHF 48.2k" variant="card">
|
|
26
|
+
<span class="flex flex-col gap-1">
|
|
27
|
+
<bf-sparkline [values]="revenue" />
|
|
28
|
+
Up 12% in 30 days
|
|
29
|
+
</span>
|
|
30
|
+
</bf-stat>
|
|
31
|
+
<bf-stat label="Sign-ups" value="1,204" variant="card">
|
|
32
|
+
<span class="flex flex-col gap-1">
|
|
33
|
+
<bf-sparkline area curve="smooth" [values]="signups" />
|
|
34
|
+
Up 4% in 30 days
|
|
35
|
+
</span>
|
|
36
|
+
</bf-stat>
|
|
37
|
+
<bf-stat label="Churn" value="2.1%" variant="card">
|
|
38
|
+
<span class="flex flex-col gap-1">
|
|
39
|
+
<bf-sparkline [values]="churn" />
|
|
40
|
+
Down 0.4 points in 30 days
|
|
41
|
+
</span>
|
|
42
|
+
</bf-stat>
|
|
43
|
+
</div>
|
|
44
|
+
`,
|
|
45
|
+
})
|
|
46
|
+
export class SparklineOverviewExampleComponent {
|
|
47
|
+
readonly revenue = [31, 34, 33, 36, 35, 39, 42, 41, 45, 48];
|
|
48
|
+
readonly signups = [980, 1010, 1060, 1040, 1100, 1120, 1150, 1170, 1190, 1204];
|
|
49
|
+
readonly churn = [2.6, 2.5, 2.6, 2.4, 2.4, 2.3, 2.3, 2.2, 2.2, 2.1];
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Overview
|
|
54
|
+
|
|
55
|
+
A tiny line of one series with no axes, legend or tooltip, for a trend beside a figure (in a
|
|
56
|
+
`bf-stat`'s supporting line, a table cell). Its scale spans the values' own range, so it shows
|
|
57
|
+
the shape of the trend, not its size; a dot marks the latest value. With a `label` it is an
|
|
58
|
+
image with that name (say what the trend means, such as "Revenue up 12% over 30 days"); without
|
|
59
|
+
one it is decorative and hidden from assistive technology. It draws in on the first render and
|
|
60
|
+
on every data change, still under reduced motion; under `dir="rtl"` it runs from the right.
|
|
61
|
+
|
|
62
|
+
A sparkline is a word-sized line of one series with no axes, legend or tooltip: the shape of a trend beside a figure. `bf-sparkline` draws it in SVG from `values`, oldest first, scaled to their own range, with a dot on the latest value. It lives in `@bitforgehq/ui/charts` (`import { BfSparklineComponent } from '@bitforgehq/ui/charts'`) and works the same in a desktop web app and in the Capacitor shell.
|
|
63
|
+
|
|
64
|
+
## When to use
|
|
65
|
+
|
|
66
|
+
Use `bf-sparkline` in a [bf-stat](https://bitforge-ui.web.app/components/stat)'s supporting line, a table cell or a list row, where the figure carries the number and the line only its direction. Use [bf-line-chart](https://bitforge-ui.web.app/components/line-chart) instead when readers need the values, the axis or more than one series.
|
|
67
|
+
|
|
68
|
+
## Area and size
|
|
69
|
+
|
|
70
|
+
`area` fills under the line; `curve="smooth"` draws a monotone curve. `height` sets the height in px (32 by default); the width follows the container, so give the element a width (`class="w-24"`) where it sits in a row. A flat series draws a level line in the middle, and a `null` value leaves a gap. The line draws in on the first render and whenever `values` change.
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
import { Component } from '@angular/core';
|
|
74
|
+
import { BfSparklineComponent } from '@bitforgehq/ui/charts';
|
|
75
|
+
|
|
76
|
+
interface Region {
|
|
77
|
+
readonly name: string;
|
|
78
|
+
readonly total: string;
|
|
79
|
+
readonly trend: readonly (number | null)[];
|
|
80
|
+
readonly summary: string;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Sparklines in a table.
|
|
85
|
+
*
|
|
86
|
+
* A fixed-width sparkline per row, each named for screen readers by what its trend means; a
|
|
87
|
+
* `null` leaves a gap where a week was not reported.
|
|
88
|
+
*/
|
|
89
|
+
@Component({
|
|
90
|
+
selector: 'app-example-sparkline-table',
|
|
91
|
+
imports: [BfSparklineComponent],
|
|
92
|
+
template: `
|
|
93
|
+
<table class="text-label-sm w-full text-start">
|
|
94
|
+
<caption class="sr-only">
|
|
95
|
+
Orders by region
|
|
96
|
+
</caption>
|
|
97
|
+
<thead>
|
|
98
|
+
<tr class="border-border border-b">
|
|
99
|
+
<th scope="col" class="text-label-xs text-text-muted h-8 px-3 text-start">Region</th>
|
|
100
|
+
<th scope="col" class="text-label-xs text-text-muted h-8 px-3 text-end">Orders</th>
|
|
101
|
+
<th scope="col" class="text-label-xs text-text-muted h-8 px-3 text-start">8 weeks</th>
|
|
102
|
+
</tr>
|
|
103
|
+
</thead>
|
|
104
|
+
<tbody>
|
|
105
|
+
@for (region of regions; track region.name) {
|
|
106
|
+
<tr class="border-divider border-b">
|
|
107
|
+
<th scope="row" class="text-text h-10 px-3 text-start font-bold">{{ region.name }}</th>
|
|
108
|
+
<td class="text-text h-10 px-3 text-end tabular-nums">{{ region.total }}</td>
|
|
109
|
+
<td class="h-10 px-3">
|
|
110
|
+
<bf-sparkline
|
|
111
|
+
class="w-24"
|
|
112
|
+
[height]="24"
|
|
113
|
+
[values]="region.trend"
|
|
114
|
+
[label]="region.summary"
|
|
115
|
+
/>
|
|
116
|
+
</td>
|
|
117
|
+
</tr>
|
|
118
|
+
}
|
|
119
|
+
</tbody>
|
|
120
|
+
</table>
|
|
121
|
+
`,
|
|
122
|
+
})
|
|
123
|
+
export class SparklineTableExampleComponent {
|
|
124
|
+
readonly regions: readonly Region[] = [
|
|
125
|
+
{
|
|
126
|
+
name: 'North',
|
|
127
|
+
total: '1,284',
|
|
128
|
+
trend: [120, 132, 128, 141, 150, 149, 162, 171],
|
|
129
|
+
summary: 'Rising',
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
name: 'Central',
|
|
133
|
+
total: '962',
|
|
134
|
+
trend: [131, 127, 119, null, 112, 118, 109, 104],
|
|
135
|
+
summary: 'Falling',
|
|
136
|
+
},
|
|
137
|
+
{
|
|
138
|
+
name: 'South',
|
|
139
|
+
total: '1,047',
|
|
140
|
+
trend: [128, 131, 126, 133, 129, 132, 130, 131],
|
|
141
|
+
summary: 'Steady',
|
|
142
|
+
},
|
|
143
|
+
];
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Desktop
|
|
148
|
+
|
|
149
|
+
A sparkline is not interactive: no hover, no tooltip, no focus stop. Its size does not follow `provideBfDefaults()`. Inside `provideBfShell()` it renders the same.
|
|
150
|
+
|
|
151
|
+
## Theming
|
|
152
|
+
|
|
153
|
+
- Line: `--color-chart-1` (`stroke-chart-1`), 2 px; area the same at 16 % (`fill-chart-1/16`); the end dot `fill-chart-1`.
|
|
154
|
+
- Motion: `--animate-chart-draw` and `--animate-chart-fade`; none under `prefers-reduced-motion`.
|
|
155
|
+
|
|
156
|
+
## Accessibility
|
|
157
|
+
|
|
158
|
+
With a `label` the SVG is a `role="img"` named by it: say what the trend means ("Revenue up 12% over 30 days"), not that it is a chart. Without one it is decorative and hidden from assistive technology, which is right beside a figure whose supporting text already states the trend. It has no focus stop and no data table; put the values in a table or a `bf-line-chart` when readers need them.
|
|
159
|
+
|
|
160
|
+
## Inputs
|
|
161
|
+
|
|
162
|
+
| Name | Type | Default | Description |
|
|
163
|
+
| -------- | --------------------------------------- | ---------- | -------------------------------------------------------------------------------------------------------------- |
|
|
164
|
+
| `values` | `readonly (number \| null)[]` | required | The values in order, oldest first; `null` leaves a gap. |
|
|
165
|
+
| `label` | `string` | `''` | What the trend means, its accessible name ("Revenue up 12% this month"); empty makes the sparkline decorative. |
|
|
166
|
+
| `height` | `number` | `32` | Height in px; the width follows the container. |
|
|
167
|
+
| `area` | `boolean` | `false` | Fills the area under the line. |
|
|
168
|
+
| `curve` | `BfChartCurve` (`'linear' \| 'smooth'`) | `'linear'` | The line between points: `linear` (default) or `smooth`, a monotone curve. |
|
|
169
|
+
|
|
170
|
+
## Notes
|
|
171
|
+
|
|
172
|
+
### Do
|
|
173
|
+
|
|
174
|
+
- State the trend in words next to it (the stat's supporting line, a `+12%`), so it is never colour or shape alone.
|
|
175
|
+
- Use the same time window for every sparkline in a row.
|
|
176
|
+
|
|
177
|
+
### Don't
|
|
178
|
+
|
|
179
|
+
- Don't compare sparklines' heights: each one is scaled to its own range.
|
|
180
|
+
- Don't put a tooltip or a link on a sparkline; link the stat or the row instead.
|
|
181
|
+
|
|
182
|
+
## Migration notes
|
|
183
|
+
|
|
184
|
+
### 0.3.1
|
|
185
|
+
|
|
186
|
+
New `bf-sparkline` in `@bitforgehq/ui/charts`: a tiny line of one series with no axes, legend or
|
|
187
|
+
tooltip, for a `bf-stat`'s supporting line or a table cell; `area`, `curve` and `height`, a dot on
|
|
188
|
+
the latest value. With a `label` it is a named image, without one it is decorative.
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
# bf-stat
|
|
2
|
+
|
|
3
|
+
One labelled figure with an optional icon tile and a supporting line
|
|
4
|
+
|
|
5
|
+
`BfStatComponent` from `@bitforgehq/ui` · Data display · since 0.3.1
|
|
6
|
+
|
|
7
|
+
## Example
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { Component } from '@angular/core';
|
|
11
|
+
import { BfStatComponent } from '@bitforgehq/ui';
|
|
12
|
+
import { BfIconComponent } from '@bitforgehq/ui/icons';
|
|
13
|
+
import { Clock, CookingPot, Timer, Users } from 'lucide';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The bf-stat overview.
|
|
17
|
+
*
|
|
18
|
+
* Live demo of `bf-stat`: a recipe's prep time, cook time, total time and servings as four card
|
|
19
|
+
* stats, each with an icon tile, wrapping from four across to two.
|
|
20
|
+
*/
|
|
21
|
+
@Component({
|
|
22
|
+
selector: 'app-example-stat-overview',
|
|
23
|
+
imports: [BfStatComponent, BfIconComponent],
|
|
24
|
+
template: `
|
|
25
|
+
<div class="grid grid-cols-[repeat(auto-fit,minmax(9rem,1fr))] gap-3">
|
|
26
|
+
<bf-stat label="Prep time" value="15 min" variant="card">
|
|
27
|
+
<bf-icon bfSlot="icon" [icon]="timer" />
|
|
28
|
+
</bf-stat>
|
|
29
|
+
<bf-stat label="Cook time" value="30 min" variant="card">
|
|
30
|
+
<bf-icon bfSlot="icon" [icon]="cookingPot" />
|
|
31
|
+
</bf-stat>
|
|
32
|
+
<bf-stat label="Total time" value="45 min" variant="card">
|
|
33
|
+
<bf-icon bfSlot="icon" [icon]="clock" />
|
|
34
|
+
</bf-stat>
|
|
35
|
+
<bf-stat label="Servings" value="4" variant="card">
|
|
36
|
+
<bf-icon bfSlot="icon" [icon]="users" />
|
|
37
|
+
</bf-stat>
|
|
38
|
+
</div>
|
|
39
|
+
`,
|
|
40
|
+
})
|
|
41
|
+
export class StatOverviewExampleComponent {
|
|
42
|
+
protected readonly timer = Timer;
|
|
43
|
+
protected readonly cookingPot = CookingPot;
|
|
44
|
+
protected readonly clock = Clock;
|
|
45
|
+
protected readonly users = Users;
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Overview
|
|
50
|
+
|
|
51
|
+
One figure with its label, such as a count, a duration or a total: a native `dl` pair with the
|
|
52
|
+
value set large, an optional icon tile at the start and a supporting line under the value.
|
|
53
|
+
|
|
54
|
+
A `delta` with a `trend` adds a pill beside the value, toned by `trendIntent`.
|
|
55
|
+
|
|
56
|
+
A stat shows one figure with its label: a recipe's cook time, a project's open tasks, a month's revenue. The value is set large, an optional icon tile sits at the start, a supporting line under the value adds context such as a period or a badge, and an optional delta pill beside the value shows the change and its direction. `bf-stat` renders its own native `dl` with the label as the `dt` and the value as the `dd`. It works the same in both shells: in the Capacitor shell and in a desktop web app.
|
|
57
|
+
|
|
58
|
+
## When to use
|
|
59
|
+
|
|
60
|
+
Use `bf-stat` for a few headline figures the reader should take in at a glance, usually in a row or grid of them. Use [bf-description-list](https://bitforge-ui.web.app/components/description-list) instead for many details of one record, and [bf-card](https://bitforge-ui.web.app/components/card) instead when the tile holds more than one figure (a title, a progress bar, actions).
|
|
61
|
+
|
|
62
|
+
## Cards
|
|
63
|
+
|
|
64
|
+
`variant="card"` gives each stat its own outlined tile, the `flat` card surface. In a grid the cards stretch to the row's height, so a row of stats lines up even when one has a supporting line and another has none. Put an icon in the `icon` slot (`bfSlot="icon"`, a `bf-icon`) to show what the figure is about.
|
|
65
|
+
|
|
66
|
+
```ts
|
|
67
|
+
import { Component } from '@angular/core';
|
|
68
|
+
import { BfStatComponent } from '@bitforgehq/ui';
|
|
69
|
+
import { BfIconComponent } from '@bitforgehq/ui/icons';
|
|
70
|
+
import { Clock, CookingPot, Timer, Users } from 'lucide';
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* The bf-stat overview.
|
|
74
|
+
*
|
|
75
|
+
* Live demo of `bf-stat`: a recipe's prep time, cook time, total time and servings as four card
|
|
76
|
+
* stats, each with an icon tile, wrapping from four across to two.
|
|
77
|
+
*/
|
|
78
|
+
@Component({
|
|
79
|
+
selector: 'app-example-stat-overview',
|
|
80
|
+
imports: [BfStatComponent, BfIconComponent],
|
|
81
|
+
template: `
|
|
82
|
+
<div class="grid grid-cols-[repeat(auto-fit,minmax(9rem,1fr))] gap-3">
|
|
83
|
+
<bf-stat label="Prep time" value="15 min" variant="card">
|
|
84
|
+
<bf-icon bfSlot="icon" [icon]="timer" />
|
|
85
|
+
</bf-stat>
|
|
86
|
+
<bf-stat label="Cook time" value="30 min" variant="card">
|
|
87
|
+
<bf-icon bfSlot="icon" [icon]="cookingPot" />
|
|
88
|
+
</bf-stat>
|
|
89
|
+
<bf-stat label="Total time" value="45 min" variant="card">
|
|
90
|
+
<bf-icon bfSlot="icon" [icon]="clock" />
|
|
91
|
+
</bf-stat>
|
|
92
|
+
<bf-stat label="Servings" value="4" variant="card">
|
|
93
|
+
<bf-icon bfSlot="icon" [icon]="users" />
|
|
94
|
+
</bf-stat>
|
|
95
|
+
</div>
|
|
96
|
+
`,
|
|
97
|
+
})
|
|
98
|
+
export class StatOverviewExampleComponent {
|
|
99
|
+
protected readonly timer = Timer;
|
|
100
|
+
protected readonly cookingPot = CookingPot;
|
|
101
|
+
protected readonly clock = Clock;
|
|
102
|
+
protected readonly users = Users;
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
## Plain
|
|
107
|
+
|
|
108
|
+
`variant="plain"` (the default) draws no surface, for stats that sit on a card or a page section of their own. The default slot is the supporting line under the value: a short trend ("+12% this week") or a `bf-badge`.
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
import { Component } from '@angular/core';
|
|
112
|
+
import { BfBadgeComponent, BfCardComponent, BfStatComponent } from '@bitforgehq/ui';
|
|
113
|
+
|
|
114
|
+
/** Plain stats on a card, with a trend and a badge as their supporting lines. */
|
|
115
|
+
@Component({
|
|
116
|
+
selector: 'app-example-stat-plain',
|
|
117
|
+
imports: [BfBadgeComponent, BfCardComponent, BfStatComponent],
|
|
118
|
+
template: `
|
|
119
|
+
<bf-card>
|
|
120
|
+
<div class="grid grid-cols-[repeat(auto-fit,minmax(9rem,1fr))] gap-4">
|
|
121
|
+
<bf-stat label="Recipes saved" value="1,204">+12% this week</bf-stat>
|
|
122
|
+
<bf-stat label="Average rating" value="4.8">From 312 reviews</bf-stat>
|
|
123
|
+
<bf-stat label="Shopping list" value="9 items">
|
|
124
|
+
<bf-badge variant="warning">3 missing</bf-badge>
|
|
125
|
+
</bf-stat>
|
|
126
|
+
</div>
|
|
127
|
+
</bf-card>
|
|
128
|
+
`,
|
|
129
|
+
})
|
|
130
|
+
export class StatPlainExampleComponent {}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Trend
|
|
134
|
+
|
|
135
|
+
`delta` (the change, already formatted, such as `+12%`) and `trend` (`up`, `down` or `flat`) add a pill beside the value; on a narrow stat it wraps under it. The pill carries an arrow mark (a chevron up or down, a dash for no change) and, for screen readers, the direction as a word before the delta ("Up +12%"), so the colour is never the only signal. An empty `delta` shows no pill; a `delta` without a `trend` shows a neutral pill with no mark.
|
|
136
|
+
|
|
137
|
+
The colour says whether the change is good, which depends on the figure. `trendIntent="positive"` (the default) is for figures where up is good, such as revenue or sign-ups: up is `success`, down is `danger`. `trendIntent="negative"` is for figures where down is good, such as costs, churn or load time: down is `success`, up is `danger`. `flat` is neutral in both. Put the comparison ("vs. last month") in the supporting line.
|
|
138
|
+
|
|
139
|
+
```ts
|
|
140
|
+
import { Component } from '@angular/core';
|
|
141
|
+
import { BfStatComponent } from '@bitforgehq/ui';
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Stats with a trend.
|
|
145
|
+
*
|
|
146
|
+
* Revenue up (good), costs down (good, so `trendIntent="negative"`) and churn unchanged, each with
|
|
147
|
+
* a delta pill beside the value.
|
|
148
|
+
*/
|
|
149
|
+
@Component({
|
|
150
|
+
selector: 'app-example-stat-trend',
|
|
151
|
+
imports: [BfStatComponent],
|
|
152
|
+
template: `
|
|
153
|
+
<div class="grid grid-cols-[repeat(auto-fit,minmax(11rem,1fr))] gap-3">
|
|
154
|
+
<bf-stat label="Revenue" value="€48,200" delta="+12%" trend="up" variant="card">
|
|
155
|
+
vs. last month
|
|
156
|
+
</bf-stat>
|
|
157
|
+
<bf-stat
|
|
158
|
+
label="Costs"
|
|
159
|
+
value="€12,900"
|
|
160
|
+
delta="-8%"
|
|
161
|
+
trend="down"
|
|
162
|
+
trendIntent="negative"
|
|
163
|
+
variant="card"
|
|
164
|
+
>
|
|
165
|
+
vs. last month
|
|
166
|
+
</bf-stat>
|
|
167
|
+
<bf-stat label="Churn" value="2.1%" delta="0%" trend="flat" variant="card">
|
|
168
|
+
vs. last month
|
|
169
|
+
</bf-stat>
|
|
170
|
+
</div>
|
|
171
|
+
`,
|
|
172
|
+
})
|
|
173
|
+
export class StatTrendExampleComponent {}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
## Desktop
|
|
177
|
+
|
|
178
|
+
A stat is not interactive: no hover, no pointer cursor, no focus stop. Its size does not follow `provideBfDefaults()`; the figure keeps the `title` type on every density. Inside `provideBfShell()` (the Capacitor shell) it renders and behaves exactly the same. A grid of stats wraps by itself with `grid-cols-[repeat(auto-fit,minmax(9rem,1fr))]` on the grid that holds them, so the same row reads as four across on a desktop and two on a phone.
|
|
179
|
+
|
|
180
|
+
## Theming
|
|
181
|
+
|
|
182
|
+
- Label (`dt`): `text-caption` in `text-muted`. Value (`dd`): `text-title` in `text`, with tabular figures (`tabular-nums`) so numbers line up across stats.
|
|
183
|
+
- Supporting line: `text-caption` in `text-muted`.
|
|
184
|
+
- Delta pill: the badge's label shape (`control` radius, `text-micro` bold, tabular figures); good changes `success-soft` with `success` text, bad ones `danger-soft` with `danger` text, flat or no trend `neutral-soft` with `neutral` text. The arrow mark is drawn with borders in `currentColor`, so it takes the pill's text colour.
|
|
185
|
+
- Icon tile: `surface-sunken` background, `text-muted` icon, `tile` radius, 40 px.
|
|
186
|
+
- `card`: `surface`, the `card` radius, `--spacing-card` padding (`p-card`) and a 1.5 px `border-strong` edge, as the `flat` `bf-card`.
|
|
187
|
+
|
|
188
|
+
## Accessibility
|
|
189
|
+
|
|
190
|
+
`bf-stat` renders a native `dl` holding the label as the `dt`, the value as a `dd` and the supporting line as a second `dd`, so a screen reader announces the label with its value and context. The icon tile sits outside the `dl`; keep the icon decorative (a `bf-icon` without a `label` is hidden from assistive technology). The delta pill sits in the value's `dd`, so it is read with the value; its arrow mark is `aria-hidden`, and a visually hidden word (the `statTrendUp`, `statTrendDown` and `statTrendFlat` labels, "Up", "Down", "No change"; translate them with `provideBfLabels()`) names the direction before the delta. The pill uses the badge's tone pairs, so its text keeps the badge's contrast in both modes; in forced colours the mark still shows, as it is drawn with borders. The stat adds no other ARIA.
|
|
191
|
+
|
|
192
|
+
## Inputs
|
|
193
|
+
|
|
194
|
+
| Name | Type | Default | Description |
|
|
195
|
+
| ------------- | -------------------------------------------------------------------- | ------------ | ---------------------------------------------------------------------------------------- |
|
|
196
|
+
| `label` | `string` | required | What the figure measures, shown above it. |
|
|
197
|
+
| `value` | `string` | required | The figure, already formatted (`45 min`, `1,204`, `98%`). |
|
|
198
|
+
| `variant` | `BfStatVariant` (`'plain' \| 'card'`) | `'plain'` | Surface: plain (default) on its container, or card, an outlined tile of its own. |
|
|
199
|
+
| `delta` | `string` | `''` | A change shown in a pill beside the value, already formatted (`+12%`); empty shows none. |
|
|
200
|
+
| `trend` | `BfStatTrend \| undefined` (`'up' \| 'down' \| 'flat' \| undefined`) | `undefined` | The delta's direction: an arrow mark and a hidden word; left unset, the pill is neutral. |
|
|
201
|
+
| `trendIntent` | `BfStatTrendIntent` (`'positive' \| 'negative'`) | `'positive'` | Whether up is good (positive, default) or down is good (negative, such as costs). |
|
|
202
|
+
|
|
203
|
+
## Slots
|
|
204
|
+
|
|
205
|
+
- `icon`: The icon in the tile at the start.
|
|
206
|
+
- `default`: A supporting line under the value, such as a trend or a badge.
|
|
207
|
+
|
|
208
|
+
## Notes
|
|
209
|
+
|
|
210
|
+
### Do
|
|
211
|
+
|
|
212
|
+
- Format the value before passing it (`45 min`, `1,204`, `98%`); the stat prints it as given.
|
|
213
|
+
- Keep the supporting line to a few words.
|
|
214
|
+
- Set `trendIntent="negative"` for figures where a fall is good news (costs, churn, errors).
|
|
215
|
+
|
|
216
|
+
### Don't
|
|
217
|
+
|
|
218
|
+
- Don't use a stat for a value the user edits or acts on; give it its own control.
|
|
219
|
+
- Don't put long text in `value`; it is set at the `title` size for a short figure.
|
|
220
|
+
- Don't put the comparison period in `delta`; keep it to the change (`+12%`) and say "vs. last month" in the supporting line.
|
|
221
|
+
|
|
222
|
+
## Migration notes
|
|
223
|
+
|
|
224
|
+
### 0.3.1
|
|
225
|
+
|
|
226
|
+
New: `bf-stat`, one labelled figure in its own `dl`, with the value at the `title` size in
|
|
227
|
+
tabular figures, an `icon` slot for a tile at the start and a supporting line in the default slot.
|
|
228
|
+
`variant="card"` gives it the outlined `flat` card surface; `plain` (default) draws none. An
|
|
229
|
+
opt-in trend: `delta` (the formatted change, such as `+12%`; empty shows nothing) with `trend`
|
|
230
|
+
(`up`, `down`, `flat`) renders a pill beside the value with a CSS arrow mark and the direction as a
|
|
231
|
+
hidden word (new labels `statTrendUp`, `statTrendDown`, `statTrendFlat`). `trendIntent`
|
|
232
|
+
(`positive` default, up is good; `negative` when down is good, such as costs) picks `success` or
|
|
233
|
+
`danger`; flat is neutral. New types `BfStatVariant`, `BfStatTrend` and `BfStatTrendIntent`.
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
# bf-tab-group
|
|
2
|
+
|
|
3
|
+
In-page tabs that switch between content panels, with keyboard navigation and badges
|
|
4
|
+
|
|
5
|
+
`BfTabGroupComponent` from `@bitforgehq/ui` · Navigation · since 0.3.1
|
|
6
|
+
|
|
7
|
+
## Example
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { Component, signal } from '@angular/core';
|
|
11
|
+
import { BfTabGroupComponent, BfTabPanelComponent } from '@bitforgehq/ui';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The bf-tab-group overview.
|
|
15
|
+
*
|
|
16
|
+
* Live demo of `bf-tab-group`: a project's Overview, Activity and Settings sections, Activity with
|
|
17
|
+
* a count badge; click a tab, or focus one and use the arrow keys, Home and End.
|
|
18
|
+
*/
|
|
19
|
+
@Component({
|
|
20
|
+
selector: 'app-example-tab-group-overview',
|
|
21
|
+
imports: [BfTabGroupComponent, BfTabPanelComponent],
|
|
22
|
+
template: `
|
|
23
|
+
<bf-tab-group label="Project sections" [(selected)]="tab">
|
|
24
|
+
<bf-tab-panel value="overview" label="Overview">
|
|
25
|
+
<p class="text-body">Atlas ships the new onboarding flow in March.</p>
|
|
26
|
+
</bf-tab-panel>
|
|
27
|
+
<bf-tab-panel value="activity" label="Activity" [badge]="3">
|
|
28
|
+
<ng-template>
|
|
29
|
+
<p class="text-body">Mara commented on the brief, and two tasks were closed.</p>
|
|
30
|
+
</ng-template>
|
|
31
|
+
</bf-tab-panel>
|
|
32
|
+
<bf-tab-panel value="settings" label="Settings">
|
|
33
|
+
<ng-template>
|
|
34
|
+
<p class="text-body">Only owners can change the project settings.</p>
|
|
35
|
+
</ng-template>
|
|
36
|
+
</bf-tab-panel>
|
|
37
|
+
</bf-tab-group>
|
|
38
|
+
<p data-status aria-live="polite" class="text-caption text-text-muted mt-3">
|
|
39
|
+
Selected tab: {{ tab() }}
|
|
40
|
+
</p>
|
|
41
|
+
`,
|
|
42
|
+
})
|
|
43
|
+
export class TabGroupOverviewExampleComponent {
|
|
44
|
+
readonly tab = signal('overview');
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## Overview
|
|
49
|
+
|
|
50
|
+
In-page tabs: a row of tabs over the content they switch, one `bf-tab-panel` per tab. The
|
|
51
|
+
`selected` model holds the shown panel's `value`; unset or unknown, the first enabled panel
|
|
52
|
+
shows. Selection follows focus: the arrow keys (mirrored under `dir="rtl"`), Home and End move
|
|
53
|
+
to a tab and show its panel. When the tabs do not fit, the row scrolls sideways.
|
|
54
|
+
|
|
55
|
+
In-page tabs: a row of tabs over the content they switch, such as Overview / Activity / Settings on a project's detail page. `bf-tab-group` holds the row and the selection; each tab and its content is a [`bf-tab-panel`](https://bitforge-ui.web.app/components/tab-panel). It works the same in a desktop web app and in the Capacitor shell.
|
|
56
|
+
|
|
57
|
+
## When to use
|
|
58
|
+
|
|
59
|
+
Use `bf-tab-group` to split one page's content into a few sections that the user switches in place, without leaving the page. In the Capacitor shell use [bf-tabs](https://bitforge-ui.web.app/components/tabs) with [bf-tab-bar](https://bitforge-ui.web.app/components/tab-bar) instead for the app's top-level destinations at the bottom of the screen: those are routes, these are sections of one page. Use [bf-top-nav](https://bitforge-ui.web.app/components/top-nav) with `a[bfNavItem]` links instead when each tab is its own route (a URL to share or open in a new tab): links are navigation, tabs switch content in place. Use [bf-segmented](https://bitforge-ui.web.app/components/segmented) for a two-to-four-way switch of one view, such as a chart's range.
|
|
60
|
+
|
|
61
|
+
## Badges and icons
|
|
62
|
+
|
|
63
|
+
A panel's `badge` adds a count pill after its tab's label, announced as "3 new"; an icon in the panel's `bfSlot="icon"` shows in the tab before the label. A `disabled` panel's tab cannot be picked.
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
import { Component } from '@angular/core';
|
|
67
|
+
import { BfTabGroupComponent, BfTabPanelComponent } from '@bitforgehq/ui';
|
|
68
|
+
import { BfIconComponent } from '@bitforgehq/ui/icons';
|
|
69
|
+
import { Activity, FileText, LayoutDashboard, Lock } from 'lucide';
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Tabs with icons, a count badge and a disabled tab.
|
|
73
|
+
*
|
|
74
|
+
* Each panel's `bfSlot="icon"` shows in its tab before the label; `badge` adds an announced
|
|
75
|
+
* count, and `disabled` takes a tab out of the selection and the keyboard navigation.
|
|
76
|
+
*/
|
|
77
|
+
@Component({
|
|
78
|
+
selector: 'app-example-tab-group-badges-and-icons',
|
|
79
|
+
imports: [BfTabGroupComponent, BfTabPanelComponent, BfIconComponent],
|
|
80
|
+
template: `
|
|
81
|
+
<bf-tab-group label="Project with icons">
|
|
82
|
+
<bf-tab-panel value="overview" label="Overview">
|
|
83
|
+
<bf-icon bfSlot="icon" [icon]="dashboard" />
|
|
84
|
+
<p class="text-body">The project at a glance.</p>
|
|
85
|
+
</bf-tab-panel>
|
|
86
|
+
<bf-tab-panel value="activity" label="Activity" [badge]="12">
|
|
87
|
+
<bf-icon bfSlot="icon" [icon]="activity" />
|
|
88
|
+
<p class="text-body">12 updates since your last visit.</p>
|
|
89
|
+
</bf-tab-panel>
|
|
90
|
+
<bf-tab-panel value="files" label="Files">
|
|
91
|
+
<bf-icon bfSlot="icon" [icon]="files" />
|
|
92
|
+
<p class="text-body">The brief and two mock-ups.</p>
|
|
93
|
+
</bf-tab-panel>
|
|
94
|
+
<bf-tab-panel value="billing" label="Billing" [disabled]="true">
|
|
95
|
+
<bf-icon bfSlot="icon" [icon]="lock" />
|
|
96
|
+
<p class="text-body">Billing is managed by the workspace owner.</p>
|
|
97
|
+
</bf-tab-panel>
|
|
98
|
+
</bf-tab-group>
|
|
99
|
+
`,
|
|
100
|
+
})
|
|
101
|
+
export class TabGroupBadgesAndIconsExampleComponent {
|
|
102
|
+
protected readonly dashboard = LayoutDashboard;
|
|
103
|
+
protected readonly activity = Activity;
|
|
104
|
+
protected readonly files = FileText;
|
|
105
|
+
protected readonly lock = Lock;
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Lazy content
|
|
110
|
+
|
|
111
|
+
Put a panel's content in an `ng-template` inside it and the content is created only while its tab is selected, and destroyed when another tab shows: a heavy chart or a feed costs nothing until it is opened. `keepAlive` keeps it in the DOM, hidden, once it has been shown, so its scroll position and form input survive switching tabs. Plain content (not in an `ng-template`) renders at once and is only hidden.
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
import { Component, signal } from '@angular/core';
|
|
115
|
+
import { BfInputComponent, BfTabGroupComponent, BfTabPanelComponent } from '@bitforgehq/ui';
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Lazy panels, one kept alive.
|
|
119
|
+
*
|
|
120
|
+
* Each panel's content is an `ng-template`, created when its tab shows. Notes is `keepAlive`:
|
|
121
|
+
* type in it, switch tabs and come back, and the text is still there; Comments is created anew
|
|
122
|
+
* each time.
|
|
123
|
+
*/
|
|
124
|
+
@Component({
|
|
125
|
+
selector: 'app-example-tab-group-lazy',
|
|
126
|
+
imports: [BfInputComponent, BfTabGroupComponent, BfTabPanelComponent],
|
|
127
|
+
template: `
|
|
128
|
+
<bf-tab-group label="Task details" [(selected)]="tab">
|
|
129
|
+
<bf-tab-panel value="notes" label="Notes" keepAlive>
|
|
130
|
+
<ng-template>
|
|
131
|
+
<bf-input label="Note" placeholder="Type, then switch tabs" />
|
|
132
|
+
</ng-template>
|
|
133
|
+
</bf-tab-panel>
|
|
134
|
+
<bf-tab-panel value="comments" label="Comments">
|
|
135
|
+
<ng-template>
|
|
136
|
+
<bf-input label="Comment" placeholder="Cleared when you leave this tab" />
|
|
137
|
+
</ng-template>
|
|
138
|
+
</bf-tab-panel>
|
|
139
|
+
</bf-tab-group>
|
|
140
|
+
`,
|
|
141
|
+
})
|
|
142
|
+
export class TabGroupLazyExampleComponent {
|
|
143
|
+
readonly tab = signal('notes');
|
|
144
|
+
}
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Selection
|
|
148
|
+
|
|
149
|
+
`[(selected)]` holds the shown panel's `value`; `(changed)` emits the new value after a user change, never for a write of `selected`. While `selected` matches no panel (unset, or a value that went away), the first enabled panel shows; `selected` itself keeps its value until the user picks a tab.
|
|
150
|
+
|
|
151
|
+
## Sizes and width
|
|
152
|
+
|
|
153
|
+
`size` sets the tabs' height: `sm` 32 px, `md` 40 px, `lg` 48 px. Unset, it follows `provideBfDefaults()`'s `controlSize`, `md` by default. `fitted` stretches the tabs to share the group's width (two or three tabs on a phone). When the tabs do not fit, the row scrolls sideways; focusing a tab with the keyboard scrolls it into view.
|
|
154
|
+
|
|
155
|
+
## Desktop
|
|
156
|
+
|
|
157
|
+
Under a mouse an unselected tab takes a `surface-muted` fill and `text` on hover, with the pointer cursor, and `surface-pressed` while pressed; a disabled tab shows `not-allowed` and does not react. The focus ring shows on keyboard focus only, on the tab and on the panel (which is in the tab order so its content can be reached and scrolled by keyboard). Under a fine pointer the tab's visual box is its hit area; under a coarse pointer (touch) every tab is at least 44 px tall. Desktop density is the default `md` step; `provideBfDefaults({ controlSize: 'sm' })` in a component's `providers` makes a dense sub-tree ([density guide](https://bitforge-ui.web.app/density)).
|
|
158
|
+
|
|
159
|
+
## Theming
|
|
160
|
+
|
|
161
|
+
- Tabs: `text-muted` at rest, hover `surface-muted` with `text`, pressed `surface-pressed`; top corners `rounded-tile`.
|
|
162
|
+
- Selected tab: `primary-text` with a 2 px `primary` underline on its bottom edge, so the selection never rests on colour alone (`CanvasText` under forced colours; `text-disabled` on a disabled selected tab).
|
|
163
|
+
- Disabled tab: `text-disabled` (never opacity).
|
|
164
|
+
- The row: a 1 px `border` line under the tabs, across the group's width.
|
|
165
|
+
- Badge: `primary` with `on-primary` text, `rounded-control`, `text-micro`.
|
|
166
|
+
- Focus ring: `ring`, on the tabs and on the panel (`rounded-tile`).
|
|
167
|
+
- Type: `text-label-xs` / `text-label-sm` / `text-label` for `sm` / `md` / `lg`, bold.
|
|
168
|
+
|
|
169
|
+
## Accessibility
|
|
170
|
+
|
|
171
|
+
The row is a `role="tablist"` named by `label`, else `provideBfLabels()`'s `tabGroup` ("Tabs"); give each group on a page its own `label`. Each tab is a native `<button role="tab">` with `aria-selected` and `aria-controls` naming its panel; each `bf-tab-panel` is a `role="tabpanel"` with `aria-labelledby` naming its tab and `tabindex="0"`, and an unselected panel is `hidden`. Only the selected tab is in the tab order (a roving tab stop), so <kbd>Tab</kbd> moves from it into the panel. Selection follows focus (automatic activation): the arrow keys, <kbd>Home</kbd> and <kbd>End</kbd> move to a tab and show its panel at once, so keep a panel's first render quick. A disabled tab is native-disabled and skipped. A badge's pill is `aria-hidden`; its count is read after the label from visually hidden text (`tabBadge(n)`, "3 new").
|
|
172
|
+
|
|
173
|
+
### Keyboard
|
|
174
|
+
|
|
175
|
+
| Key | Action |
|
|
176
|
+
| ------------------------------------------------ | ---------------------------------------------------------------------------------------------------- |
|
|
177
|
+
| <kbd>Tab</kbd> / <kbd>Shift</kbd>+<kbd>Tab</kbd> | Moves focus to the selected tab, then into the shown panel, and out again |
|
|
178
|
+
| <kbd>ArrowRight</kbd> | Selects and focuses the next enabled tab, wrapping at the end (the previous one under `dir="rtl"`) |
|
|
179
|
+
| <kbd>ArrowLeft</kbd> | Selects and focuses the previous enabled tab, wrapping at the start (the next one under `dir="rtl"`) |
|
|
180
|
+
| <kbd>Home</kbd> / <kbd>End</kbd> | Selects and focuses the first / last enabled tab |
|
|
181
|
+
| <kbd>Enter</kbd> / <kbd>Space</kbd> | Selects the focused tab (it is already selected after an arrow key) |
|
|
182
|
+
|
|
183
|
+
## Inputs
|
|
184
|
+
|
|
185
|
+
| Name | Type | Default | Description |
|
|
186
|
+
| ---------- | ----------------------------------------------------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
187
|
+
| `label` | `string` | `''` | Accessible name of the tablist; empty falls back to `provideBfLabels()`'s `tabGroup` ("Tabs"). |
|
|
188
|
+
| `size` | `BfSize \| undefined` (`'sm' \| 'md' \| 'lg' \| undefined`) | `undefined` | Height step of the tabs: sm 32, md 40, lg 48 px (at least 44 px under a coarse pointer); unset follows `provideBfDefaults()` (`controlSize`, `md` by default). |
|
|
189
|
+
| `fitted` | `boolean` | `false` | Stretches the tabs to share the group's width. |
|
|
190
|
+
| `selected` | `string` | `''` | The shown panel's `value`; two-way bindable. Unset or unknown shows the first enabled panel (the model keeps its value until the user picks a tab). Two-way: `[(selected)]`. |
|
|
191
|
+
|
|
192
|
+
## Outputs
|
|
193
|
+
|
|
194
|
+
| Name | Payload | Description |
|
|
195
|
+
| --------- | -------- | -------------------------------------------------------------------- |
|
|
196
|
+
| `changed` | `string` | Emitted with the newly selected panel's `value` after a user change. |
|
|
197
|
+
|
|
198
|
+
## Slots
|
|
199
|
+
|
|
200
|
+
- `default`: The panels, each a `bf-tab-panel`.
|
|
201
|
+
|
|
202
|
+
## Notes
|
|
203
|
+
|
|
204
|
+
### Do
|
|
205
|
+
|
|
206
|
+
- Keep tab labels to one or two short words, and the tabs to a handful that fit the usual width.
|
|
207
|
+
- Put heavy content in an `ng-template` so it renders only when its tab is opened.
|
|
208
|
+
|
|
209
|
+
### Don't
|
|
210
|
+
|
|
211
|
+
- Don't use tabs to navigate between pages; use `bf-top-nav` links, which the router and the browser understand.
|
|
212
|
+
- Don't nest a tab group in another tab group's panel; split the page instead.
|
|
213
|
+
|
|
214
|
+
## Migration notes
|
|
215
|
+
|
|
216
|
+
### 0.3.1
|
|
217
|
+
|
|
218
|
+
New `bf-tab-group`: in-page tabs over the content they switch, in both shells. A `tablist` of
|
|
219
|
+
`button role="tab"` from its `bf-tab-panel` children, the `selected` model of the shown panel's
|
|
220
|
+
`value` with `changed` after a user change (unset or unknown shows the first enabled panel),
|
|
221
|
+
named by `label` (else the new `tabGroup` label, "Tabs"), `size` from `controlSize` (44 px at
|
|
222
|
+
least under a coarse pointer) and `fitted` to share the width. A roving tab stop and automatic
|
|
223
|
+
activation: the arrow keys (mirrored under `dir="rtl"`), Home and End select and focus a tab,
|
|
224
|
+
skipping disabled ones. The selected tab is underlined in `primary`; the row scrolls sideways
|
|
225
|
+
when the tabs do not fit. New `BfTabGroupHarness` in `@bitforgehq/ui/testing` (`getTabLabels()`,
|
|
226
|
+
`getSelectedLabel()`, `selectTab(label)`, `getPanelText()`).
|