@bitforgehq/ui 0.3.0 → 0.3.2
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 +598 -0
- package/README.md +18 -11
- package/ai/MANIFEST.index.json +306 -0
- package/ai/MANIFEST.json +8915 -3788
- package/ai/REFERENCE.index.json +476 -0
- package/ai/REFERENCE.json +5810 -2905
- package/ai/UI_GUIDE.md +212 -58
- package/ai/components/app-frame.md +328 -0
- 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 +119 -4
- 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/empty-state.md +99 -6
- package/ai/components/header.md +71 -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-toggle.md +287 -0
- package/ai/components/sidebar.md +541 -0
- package/ai/components/skeleton.md +215 -0
- package/ai/components/sparkline.md +188 -0
- package/ai/components/stat.md +297 -0
- package/ai/components/tab-group.md +226 -0
- package/ai/components/tab-panel.md +163 -0
- package/ai/components/table.md +697 -0
- package/ai/components/tabs.md +46 -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 +354 -0
- package/ai/components/tooltip.md +230 -0
- package/ai/components/top-nav.md +159 -0
- package/ai/reference/bf-app-frame-breakpoint.md +27 -0
- package/ai/reference/bf-app-frame-content-width.md +29 -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-empty-state-heading-level.md +1 -1
- package/ai/reference/bf-empty-state-heading.md +33 -0
- package/ai/reference/bf-labels.md +73 -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-sidebar-state-service.md +21 -0
- package/ai/reference/bf-sidebar-toggle-harness.md +32 -0
- package/ai/reference/bf-size.md +13 -0
- package/ai/reference/bf-skeleton-shape.md +28 -0
- package/ai/reference/bf-stat-appearance.md +33 -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-table-stack.md +29 -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_sidebar_state_storage.md +20 -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 +13 -4
- package/ai/reference/provide-bf-sidebar-state.md +20 -0
- package/ai/reference/slide-in-from-end.md +24 -0
- package/ai/reference/slide-out-to-end.md +24 -0
- package/bin/bf-ui.js +433 -3
- 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 +6566 -38
- package/fesm2022/bitforgehq-ui-overlay.mjs.map +1 -1
- package/fesm2022/bitforgehq-ui-testing.mjs +634 -1
- package/fesm2022/bitforgehq-ui-testing.mjs.map +1 -1
- package/fesm2022/bitforgehq-ui.mjs +5708 -137
- package/fesm2022/bitforgehq-ui.mjs.map +1 -1
- package/llms-full.txt +26766 -14470
- package/llms.txt +106 -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 +1895 -7
- package/types/bitforgehq-ui-testing.d.ts +782 -1
- package/types/bitforgehq-ui.d.ts +2393 -45
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
# bf-skeleton
|
|
2
|
+
|
|
3
|
+
Loading placeholder shaped as text lines, a rectangle or a circle, with a shimmer
|
|
4
|
+
|
|
5
|
+
`BfSkeletonComponent` from `@bitforgehq/ui` · Feedback · since 0.3.1
|
|
6
|
+
|
|
7
|
+
## Example
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { Component } from '@angular/core';
|
|
11
|
+
import { BfSkeletonComponent } from '@bitforgehq/ui';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The bf-skeleton overview.
|
|
15
|
+
*
|
|
16
|
+
* Live demo of `bf-skeleton`: the three shapes (a circle for an avatar, text lines with a shorter
|
|
17
|
+
* last line, a rect for an image) inside a region marked `aria-busy="true"` while it loads.
|
|
18
|
+
*/
|
|
19
|
+
@Component({
|
|
20
|
+
selector: 'app-example-skeleton-overview',
|
|
21
|
+
imports: [BfSkeletonComponent],
|
|
22
|
+
template: `
|
|
23
|
+
<div aria-busy="true" class="flex max-w-md flex-col gap-4">
|
|
24
|
+
<div class="flex items-center gap-3">
|
|
25
|
+
<bf-skeleton shape="circle" />
|
|
26
|
+
<div class="flex min-w-0 flex-1 flex-col gap-2">
|
|
27
|
+
<bf-skeleton width="40%" height="0.875rem" />
|
|
28
|
+
<bf-skeleton width="25%" />
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
<bf-skeleton [lines]="3" height="0.875rem" />
|
|
32
|
+
<bf-skeleton shape="rect" height="8rem" />
|
|
33
|
+
</div>
|
|
34
|
+
`,
|
|
35
|
+
})
|
|
36
|
+
export class SkeletonOverviewExampleComponent {}
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Overview
|
|
40
|
+
|
|
41
|
+
Loading placeholder in the shape of the content to come: text lines, a rectangle or a circle,
|
|
42
|
+
on the sunken surface with a shimmer. It is hidden from assistive technology; mark the region it
|
|
43
|
+
stands in for with `aria-busy="true"` while it loads.
|
|
44
|
+
|
|
45
|
+
A skeleton is a placeholder in the shape of the content that is still loading: a few text lines, a rectangle for an image or a chart, a circle for an avatar. It sits on the `surface-sunken` fill with a soft shimmer, so the page keeps its layout while the data arrives and nothing jumps when it does. `bf-skeleton` is hidden from assistive technology; the region it stands in for says that it is busy. It works the same in both shells: in a desktop web app and in the Capacitor shell.
|
|
46
|
+
|
|
47
|
+
## When to use
|
|
48
|
+
|
|
49
|
+
Use `bf-skeleton` while a screen or a section loads content whose shape you know: a card, a list, the rows of a table. Use [bf-spinner](https://bitforge-ui.web.app/components/spinner) instead for a short wait with no known shape, or inside a button, and [bf-progress](https://bitforge-ui.web.app/components/progress) when you can say how far the work is. Once loading has failed, replace the skeletons with a [bf-alert](https://bitforge-ui.web.app/components/alert); when it returned nothing, with a [bf-empty-state](https://bitforge-ui.web.app/components/empty-state).
|
|
50
|
+
|
|
51
|
+
## Shapes and sizes
|
|
52
|
+
|
|
53
|
+
`shape` picks the placeholder. `text` (the default) draws one line, fully rounded; `lines` draws several, the last one shorter, as a paragraph's last line is. `rect` is one bar with the field radius, for an image, a chart or a field. `circle` is a disc for an avatar.
|
|
54
|
+
|
|
55
|
+
`width` and `height` take CSS lengths (`12rem`, `60%`, `48px`). Without a `width` a text or rect skeleton fills its container and a circle is 40 px; a circle's height always follows its width. `height` is the height of each text line (default `0.75rem`) or of the rect (default `6rem`); a circle ignores it. Match the size of the content to come, such as `0.875rem` lines for `text-body`, so the page does not move when it arrives.
|
|
56
|
+
|
|
57
|
+
## A card while it loads
|
|
58
|
+
|
|
59
|
+
Build the placeholder from the same layout as the loaded card: the same `bf-card`, the same gaps, a skeleton where each piece of content goes. Mark the region the skeletons stand in for with `aria-busy="true"` while it loads, and drop the attribute (or set it to `false`) when the content is in.
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
import { Component } from '@angular/core';
|
|
63
|
+
import { BfAvatarComponent, BfCardComponent, BfSkeletonComponent } from '@bitforgehq/ui';
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* A card while it loads, beside the loaded card.
|
|
67
|
+
*
|
|
68
|
+
* The loading card has the loaded one's layout, with a skeleton where each piece of content goes:
|
|
69
|
+
* a 40 px circle for the avatar, two short lines for the name and the role, three lines of text.
|
|
70
|
+
* Only the loading card carries `aria-busy="true"`.
|
|
71
|
+
*/
|
|
72
|
+
@Component({
|
|
73
|
+
selector: 'app-example-skeleton-card',
|
|
74
|
+
imports: [BfCardComponent, BfAvatarComponent, BfSkeletonComponent],
|
|
75
|
+
template: `
|
|
76
|
+
<div class="grid grid-cols-[repeat(auto-fit,minmax(15rem,1fr))] gap-4">
|
|
77
|
+
<bf-card aria-busy="true">
|
|
78
|
+
<div class="flex flex-col gap-4">
|
|
79
|
+
<div class="flex items-center gap-3">
|
|
80
|
+
<bf-skeleton shape="circle" />
|
|
81
|
+
<div class="flex min-w-0 flex-1 flex-col gap-2">
|
|
82
|
+
<bf-skeleton width="55%" height="1rem" />
|
|
83
|
+
<bf-skeleton width="35%" height="0.75rem" />
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
86
|
+
<bf-skeleton [lines]="3" height="0.875rem" />
|
|
87
|
+
</div>
|
|
88
|
+
</bf-card>
|
|
89
|
+
<bf-card>
|
|
90
|
+
<div class="flex flex-col gap-4">
|
|
91
|
+
<div class="flex items-center gap-3">
|
|
92
|
+
<bf-avatar initials="MK" />
|
|
93
|
+
<div class="flex min-w-0 flex-col">
|
|
94
|
+
<p class="text-label text-text">Mara Keller</p>
|
|
95
|
+
<p class="text-caption text-text-muted">Design lead</p>
|
|
96
|
+
</div>
|
|
97
|
+
</div>
|
|
98
|
+
<p class="text-body text-text-muted">
|
|
99
|
+
Runs the Atlas rebrand and the design reviews on Thursdays. Ask her about the type
|
|
100
|
+
scale.
|
|
101
|
+
</p>
|
|
102
|
+
</div>
|
|
103
|
+
</bf-card>
|
|
104
|
+
</div>
|
|
105
|
+
`,
|
|
106
|
+
})
|
|
107
|
+
export class SkeletonCardExampleComponent {}
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
## Table rows
|
|
111
|
+
|
|
112
|
+
A loading `bf-table` keeps its real header and shows skeleton rows: pass a few placeholder rows and render a `bf-skeleton` in each column through `bfTableCell` templates, with widths that differ from row to row. The header tells the user what is coming; the table's wrapper carries `aria-busy="true"`. In a list, render a few `bf-list-item`s with a circle and two lines the same way.
|
|
113
|
+
|
|
114
|
+
```ts
|
|
115
|
+
import { Component } from '@angular/core';
|
|
116
|
+
import {
|
|
117
|
+
BfSkeletonComponent,
|
|
118
|
+
BfTableCellDirective,
|
|
119
|
+
BfTableComponent,
|
|
120
|
+
type BfTableColumn,
|
|
121
|
+
} from '@bitforgehq/ui';
|
|
122
|
+
|
|
123
|
+
interface PlaceholderRow {
|
|
124
|
+
id: string;
|
|
125
|
+
/** The width of each column's skeleton in this row, so the rows do not look stamped. */
|
|
126
|
+
widths: Readonly<Record<'title' | 'client' | 'amount', string>>;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Skeleton rows in a bf-table.
|
|
131
|
+
*
|
|
132
|
+
* The table keeps its real header while the rows load: four placeholder rows render a
|
|
133
|
+
* `bf-skeleton` in every column through `bfTableCell` templates, at widths that differ from row to
|
|
134
|
+
* row, and the wrapper carries `aria-busy="true"`.
|
|
135
|
+
*/
|
|
136
|
+
@Component({
|
|
137
|
+
selector: 'app-example-skeleton-table',
|
|
138
|
+
imports: [BfTableComponent, BfTableCellDirective, BfSkeletonComponent],
|
|
139
|
+
template: `
|
|
140
|
+
<div aria-busy="true">
|
|
141
|
+
<bf-table label="Invoices" [rows]="rows" [columns]="columns">
|
|
142
|
+
<ng-template bfTableCell="title" [bfTableCellOf]="rows" let-row>
|
|
143
|
+
<bf-skeleton [width]="row.widths.title" height="0.875rem" />
|
|
144
|
+
</ng-template>
|
|
145
|
+
<ng-template bfTableCell="client" [bfTableCellOf]="rows" let-row>
|
|
146
|
+
<bf-skeleton [width]="row.widths.client" height="0.875rem" />
|
|
147
|
+
</ng-template>
|
|
148
|
+
<ng-template bfTableCell="amount" [bfTableCellOf]="rows" let-row>
|
|
149
|
+
<bf-skeleton class="ms-auto" [width]="row.widths.amount" height="0.875rem" />
|
|
150
|
+
</ng-template>
|
|
151
|
+
</bf-table>
|
|
152
|
+
</div>
|
|
153
|
+
`,
|
|
154
|
+
})
|
|
155
|
+
export class SkeletonTableExampleComponent {
|
|
156
|
+
readonly columns: readonly BfTableColumn[] = [
|
|
157
|
+
{ key: 'title', label: 'Invoice' },
|
|
158
|
+
{ key: 'client', label: 'Client' },
|
|
159
|
+
{ key: 'amount', label: 'Amount', align: 'end' },
|
|
160
|
+
];
|
|
161
|
+
readonly rows: readonly PlaceholderRow[] = [
|
|
162
|
+
{ id: 'a', widths: { title: '70%', client: '55%', amount: '4rem' } },
|
|
163
|
+
{ id: 'b', widths: { title: '55%', client: '70%', amount: '3rem' } },
|
|
164
|
+
{ id: 'c', widths: { title: '80%', client: '45%', amount: '4.5rem' } },
|
|
165
|
+
{ id: 'd', widths: { title: '60%', client: '60%', amount: '3.5rem' } },
|
|
166
|
+
];
|
|
167
|
+
}
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
## Desktop
|
|
171
|
+
|
|
172
|
+
A skeleton is not interactive: no hover, no pointer cursor, no focus stop, and its size does not follow `provideBfDefaults()`. On desktop and in the Capacitor shell it looks and behaves the same. Show skeletons only for a wait that lasts: when a load usually finishes in a few hundred milliseconds, start showing them after a short delay (a timer in the state service), so a fast connection never sees a flash of placeholders.
|
|
173
|
+
|
|
174
|
+
## Theming
|
|
175
|
+
|
|
176
|
+
- Fill: `surface-sunken`. Shimmer: a `surface` highlight at 70 % opacity that sweeps across from the inline start, with `--animate-skeleton-shimmer` (1.6 s, ease-in-out, endless) and `--animate-skeleton-shimmer-rtl` under `dir="rtl"`.
|
|
177
|
+
- Radius: text lines and the circle `rounded-full`; the rect `rounded-field`.
|
|
178
|
+
- Under `prefers-reduced-motion: reduce` the shimmer is hidden and the placeholder stays still. Under forced colours a `GrayText` border draws the shape, as backgrounds are dropped there.
|
|
179
|
+
|
|
180
|
+
## Accessibility
|
|
181
|
+
|
|
182
|
+
Every `bf-skeleton` carries `aria-hidden="true"`: a placeholder has nothing to say, and a screen reader that read out a row of shapes would only make noise. Say that the content is loading on the region it stands in for instead: `aria-busy="true"` on that element tells assistive technology the region is being updated, and dropping it lets the finished content be read. For a load the user started and waits for (a search), also put a short polite status in the page, such as "Loading results", from a `role="status"` element or a `bf-spinner`, which announces its `label` (default "Loading"). The skeleton takes no focus, so keyboard focus stays where the user left it.
|
|
183
|
+
|
|
184
|
+
## Inputs
|
|
185
|
+
|
|
186
|
+
| Name | Type | Default | Description |
|
|
187
|
+
| -------- | -------------------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------- |
|
|
188
|
+
| `shape` | `BfSkeletonShape` (`'text' \| 'rect' \| 'circle'`) | `'text'` | What it stands in for: `text` lines (default), a `rect` (an image, a chart) or a `circle` (an avatar). |
|
|
189
|
+
| `lines` | `number` | `1` | How many lines a `text` skeleton draws, the last one shorter when there are several; ignored by the other shapes. |
|
|
190
|
+
| `width` | `string` | `''` | Width as a CSS length (`12rem`, `60%`); empty fills the container, a circle is 40 px. A circle's height follows its width. |
|
|
191
|
+
| `height` | `string` | `''` | Height as a CSS length: of each line of `text` (default `0.75rem`) or of the `rect` (default `6rem`); a circle ignores it. |
|
|
192
|
+
|
|
193
|
+
## Notes
|
|
194
|
+
|
|
195
|
+
### Do
|
|
196
|
+
|
|
197
|
+
- Shape the skeleton like the content to come, at its size, so nothing jumps when it loads.
|
|
198
|
+
- Put `aria-busy="true"` on the loading region, and remove it once the content is there.
|
|
199
|
+
- Show a few rows or cards, not a whole page of them.
|
|
200
|
+
|
|
201
|
+
### Don't
|
|
202
|
+
|
|
203
|
+
- Don't show a skeleton for a wait that has no known shape; use a `bf-spinner`.
|
|
204
|
+
- Don't leave skeletons up after a load fails or returns nothing; show the error or the empty state.
|
|
205
|
+
|
|
206
|
+
## Migration notes
|
|
207
|
+
|
|
208
|
+
### 0.3.1
|
|
209
|
+
|
|
210
|
+
New `bf-skeleton` in `@bitforgehq/ui`, a loading placeholder for both shells: `shape` (the new
|
|
211
|
+
`BfSkeletonShape`: `text` lines, a `rect` or a `circle`), `lines` for several text lines with a
|
|
212
|
+
shorter last one, and `width` / `height` as CSS lengths. It draws the `surface-sunken` fill with a
|
|
213
|
+
`surface` shimmer that sweeps from the inline start (hidden under reduced motion) and carries
|
|
214
|
+
`aria-hidden="true"`; the docs mark the loading region with `aria-busy="true"` and show a card and
|
|
215
|
+
a table of skeleton rows.
|
|
@@ -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.
|