@bitforgehq/ui 0.3.3 → 0.5.0-next.3
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 +394 -0
- package/README.md +14 -14
- package/ai/MANIFEST.index.json +101 -2
- package/ai/MANIFEST.json +2630 -746
- package/ai/REFERENCE.index.json +196 -0
- package/ai/REFERENCE.json +1625 -140
- package/ai/UI_GUIDE.md +83 -21
- package/ai/components/app-frame.md +1 -1
- package/ai/components/badge.md +39 -2
- package/ai/components/chat-composer.md +357 -0
- package/ai/components/chat-message.md +251 -0
- package/ai/components/chat-thread.md +521 -0
- package/ai/components/checkbox.md +7 -0
- package/ai/components/combobox.md +12 -0
- package/ai/components/date-input.md +9 -0
- package/ai/components/datetime-input.md +2 -0
- package/ai/components/field.md +10 -0
- package/ai/components/file-input.md +498 -0
- package/ai/components/header.md +1 -1
- package/ai/components/image-input.md +368 -0
- package/ai/components/image-viewer.md +309 -0
- package/ai/components/list-item.md +105 -9
- package/ai/components/media-card.md +478 -0
- package/ai/components/menu-button.md +2 -0
- package/ai/components/menu.md +10 -0
- package/ai/components/multi-combobox.md +7 -0
- package/ai/components/nav-group.md +10 -3
- package/ai/components/nav-item.md +11 -2
- package/ai/components/number-input.md +2 -7
- package/ai/components/popover.md +13 -0
- package/ai/components/radio-group.md +6 -0
- package/ai/components/segmented.md +14 -2
- package/ai/components/select.md +100 -42
- package/ai/components/sidebar-toggle.md +16 -9
- package/ai/components/sidebar.md +41 -13
- package/ai/components/step.md +112 -0
- package/ai/components/steps.md +124 -0
- package/ai/components/tab-group.md +18 -2
- package/ai/components/table.md +115 -4
- package/ai/components/theme-menu-button.md +1 -0
- package/ai/components/time-input.md +2 -0
- package/ai/components/timeline-event.md +144 -0
- package/ai/components/timeline.md +201 -0
- package/ai/components/toggle.md +11 -1
- package/ai/components/toolbar.md +7 -18
- package/ai/reference/bf-action-directive.md +3 -1
- package/ai/reference/bf-badge-emphasis.md +27 -0
- package/ai/reference/bf-chat-composer-harness.md +64 -0
- package/ai/reference/bf-chat-message-role.md +27 -0
- package/ai/reference/bf-chat-message-status.md +28 -0
- package/ai/reference/bf-fade-in-directive.md +18 -0
- package/ai/reference/bf-file-input-harness.md +67 -0
- package/ai/reference/bf-file-input-variant.md +27 -0
- package/ai/reference/bf-image-input-harness.md +67 -0
- package/ai/reference/bf-image-input-shape.md +27 -0
- package/ai/reference/bf-image-viewer-actions-context.md +29 -0
- package/ai/reference/bf-image-viewer-harness.md +87 -0
- package/ai/reference/bf-image-viewer-image.md +25 -0
- package/ai/reference/bf-image-viewer-options.md +33 -0
- package/ai/reference/bf-image-viewer-origin.md +23 -0
- package/ai/reference/bf-image-viewer-ref.md +55 -0
- package/ai/reference/bf-image-viewer-service.md +32 -0
- package/ai/reference/bf-labels.md +105 -74
- package/ai/reference/bf-media-card-aspect.md +28 -0
- package/ai/reference/bf-media-card-harness.md +66 -0
- package/ai/reference/bf-motion-list-directive.md +22 -0
- package/ai/reference/bf-prose-directive.md +16 -0
- package/ai/reference/bf-rejected-file-reason.md +28 -0
- package/ai/reference/bf-rejected-file.md +28 -0
- package/ai/reference/bf-select-harness.md +14 -5
- package/ai/reference/bf-select-option.md +2 -0
- package/ai/reference/bf-size.md +3 -0
- package/ai/reference/bf-step-heading-level.md +28 -0
- package/ai/reference/bf-table-align.md +1 -1
- package/ai/reference/bf-table-column-align.md +33 -0
- package/ai/reference/bf-table-column.md +7 -7
- package/ai/reference/bf-table-harness.md +12 -0
- package/ai/reference/bf-timeline-tone.md +31 -0
- package/ai/reference/bf-toggle-harness.md +55 -0
- package/ai/reference/bf_table_sort_select.md +23 -0
- package/ai/reference/provide-bf-labels.md +10 -3
- package/ai/reference/provide-bf-select.md +20 -0
- package/bin/bf-ui.js +1 -0
- package/dark-ink.css +124 -0
- package/fesm2022/bitforgehq-ui-mobile.mjs +2 -1
- package/fesm2022/bitforgehq-ui-mobile.mjs.map +1 -1
- package/fesm2022/bitforgehq-ui-overlay.mjs +2649 -606
- package/fesm2022/bitforgehq-ui-overlay.mjs.map +1 -1
- package/fesm2022/bitforgehq-ui-testing.mjs +406 -11
- package/fesm2022/bitforgehq-ui-testing.mjs.map +1 -1
- package/fesm2022/bitforgehq-ui.mjs +11149 -6957
- package/fesm2022/bitforgehq-ui.mjs.map +1 -1
- package/llms-full.txt +5219 -141
- package/llms.txt +40 -1
- package/package.json +2 -1
- package/theme.css +84 -0
- package/types/bitforgehq-ui-overlay.d.ts +684 -7
- package/types/bitforgehq-ui-testing.d.ts +510 -7
- package/types/bitforgehq-ui.d.ts +1757 -186
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
# bf-timeline
|
|
2
|
+
|
|
3
|
+
Dated list of events with who did what and when, on a vertical line
|
|
4
|
+
|
|
5
|
+
`BfTimelineComponent` from `@bitforgehq/ui` · Data display · since 0.4.0
|
|
6
|
+
|
|
7
|
+
## Example
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { Component } from '@angular/core';
|
|
11
|
+
import { RouterLink } from '@angular/router';
|
|
12
|
+
import { BfTimelineComponent, BfTimelineEventComponent } from '@bitforgehq/ui';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The bf-timeline overview.
|
|
16
|
+
*
|
|
17
|
+
* Live demo of `bf-timeline`: the history of a security finding, newest first. A system event has
|
|
18
|
+
* no actor, the pull request is a link in the event text, the failed CI run is `danger` and the
|
|
19
|
+
* fix `success`.
|
|
20
|
+
*/
|
|
21
|
+
@Component({
|
|
22
|
+
selector: 'app-example-timeline-overview',
|
|
23
|
+
imports: [RouterLink, BfTimelineComponent, BfTimelineEventComponent],
|
|
24
|
+
template: `
|
|
25
|
+
<bf-timeline label="Finding history" class="max-w-xl">
|
|
26
|
+
<bf-timeline-event actor="Lena Meier" time="2026-10-28T15:40:00Z" tone="success">
|
|
27
|
+
Marked the finding as fixed
|
|
28
|
+
</bf-timeline-event>
|
|
29
|
+
<bf-timeline-event actor="CI" time="2026-10-28T13:05:00Z" tone="danger">
|
|
30
|
+
CI failed on the security scan
|
|
31
|
+
</bf-timeline-event>
|
|
32
|
+
<bf-timeline-event actor="Jonas Keller" time="2026-10-28T11:20:00Z" tone="primary">
|
|
33
|
+
Linked
|
|
34
|
+
<a routerLink="." fragment="links-in-events">PR #123</a>
|
|
35
|
+
to the finding
|
|
36
|
+
</bf-timeline-event>
|
|
37
|
+
<bf-timeline-event actor="Jonas Keller" time="2026-10-27T16:02:00Z">
|
|
38
|
+
Assigned the finding to Lena Meier
|
|
39
|
+
</bf-timeline-event>
|
|
40
|
+
<bf-timeline-event time="2026-10-27T08:14:00Z">
|
|
41
|
+
Created by the nightly dependency scan
|
|
42
|
+
</bf-timeline-event>
|
|
43
|
+
</bf-timeline>
|
|
44
|
+
`,
|
|
45
|
+
})
|
|
46
|
+
export class TimelineOverviewExampleComponent {}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Overview
|
|
50
|
+
|
|
51
|
+
A dated history of events, newest first or oldest first as the consumer orders them: who did
|
|
52
|
+
what and when, each event a `bf-timeline-event` with a marker on a vertical line.
|
|
53
|
+
|
|
54
|
+
A timeline shows the history of one thing as dated events: who did what, and when. Each event is a [`bf-timeline-event`](https://bitforge-ui.web.app/components/timeline-event) with a coloured marker on a vertical line, its text (which may link the objects it names), then the actor and the time. The timeline shows the events in the order you give them, newest first or oldest first; it never sorts. It works the same in a desktop web app, a narrow side panel and the Capacitor shell.
|
|
55
|
+
|
|
56
|
+
## When to use
|
|
57
|
+
|
|
58
|
+
Use `bf-timeline` for a record's history or an audit trail: a finding that was created, assigned, linked to a pull request and fixed; the changes in a CVE side panel; who changed a setting. Use [bf-list](https://bitforge-ui.web.app/components/list) instead for items the user opens or acts on, and for navigation; a timeline is not a stepper through a process either, nor a feed the user scrolls for new items. For the details of one record, use a [bf-description-list](https://bitforge-ui.web.app/components/description-list).
|
|
59
|
+
|
|
60
|
+
## Events and tones
|
|
61
|
+
|
|
62
|
+
`actor` names who did it (leave it empty for a system event), `time` says when (a `Date` or an ISO 8601 string), and the content is the event text. `tone` colours the marker: `neutral` (the default) for routine events, `primary` for the current or most important one, `success` (fixed, merged), `warning`, `danger` (CI failed, a regression) and `info`. The colour only supports the text, so say what happened in words ("CI failed"), never by the colour alone. The line runs from each marker to the next and ends at the last event.
|
|
63
|
+
|
|
64
|
+
## Links in events
|
|
65
|
+
|
|
66
|
+
The event text is the default slot, so the objects it names can be links: a pull request, a repository, a CVE. Put a plain `<a href>` (or `<a routerLink>`) inline in the text; the event styles every link in its text in `primary-text`, underlined, with a thicker underline on hover and the focus ring on keyboard focus. Keep the link text the object's name ("PR #123"), not "here".
|
|
67
|
+
|
|
68
|
+
## Time format
|
|
69
|
+
|
|
70
|
+
The time is formatted with `Intl.DateTimeFormat` in the app's `LOCALE_ID`: a medium date and a short time by default (`28 Oct 2026, 14:30` in `en-GB`, `28.10.2026, 14:30` in `de-CH`), in the browser's time zone. `timeFormat` on the timeline takes any `Intl.DateTimeFormatOptions` for all its events (`{ dateStyle: 'short' }`, or a `timeZone`), and `locale` overrides the language for one timeline. `timeText` on an event shows your own text in place of the formatted time, such as a relative "vor 2 Std."; the `time` element's `datetime` keeps the exact value. An empty or invalid `time` shows no time, and a locale or options the browser refuses fall back to the defaults, so a bad value never throws.
|
|
71
|
+
|
|
72
|
+
## Audit trail
|
|
73
|
+
|
|
74
|
+
An audit trail is actor-heavy: every event names a person or a bot. The `end` slot holds a small action for one event, such as a Show diff button: it sits at the inline end while the timeline is wide, and wraps under the text in a narrow side panel or on a phone (once the text would get narrower than 16rem). It takes no space while empty.
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
import { Component, signal } from '@angular/core';
|
|
78
|
+
import { BfButtonComponent, BfTimelineComponent, BfTimelineEventComponent } from '@bitforgehq/ui';
|
|
79
|
+
|
|
80
|
+
interface AuditEntry {
|
|
81
|
+
id: string;
|
|
82
|
+
actor: string;
|
|
83
|
+
action: string;
|
|
84
|
+
time: string;
|
|
85
|
+
ago: string;
|
|
86
|
+
diff: boolean;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const ENTRIES: readonly AuditEntry[] = [
|
|
90
|
+
{
|
|
91
|
+
id: 'severity',
|
|
92
|
+
actor: 'Lena Meier',
|
|
93
|
+
action: 'Raised the severity from Medium to High',
|
|
94
|
+
time: '2026-10-28T14:10:00Z',
|
|
95
|
+
ago: '2 hours ago',
|
|
96
|
+
diff: true,
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
id: 'policy',
|
|
100
|
+
actor: 'Policy bot',
|
|
101
|
+
action: 'Applied the "Critical CVEs block releases" policy',
|
|
102
|
+
time: '2026-10-28T09:30:00Z',
|
|
103
|
+
ago: '7 hours ago',
|
|
104
|
+
diff: true,
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
id: 'owner',
|
|
108
|
+
actor: 'Jonas Keller',
|
|
109
|
+
action: 'Set the owner to the Platform team',
|
|
110
|
+
time: '2026-10-27T16:45:00Z',
|
|
111
|
+
ago: 'Yesterday',
|
|
112
|
+
diff: false,
|
|
113
|
+
},
|
|
114
|
+
];
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* An audit trail with relative times and an action per event.
|
|
118
|
+
*
|
|
119
|
+
* Every event names its actor, `timeText` shows a relative time while `datetime` keeps the exact
|
|
120
|
+
* one, and the `end` slot holds a Show diff button: at the inline end while the timeline is wide,
|
|
121
|
+
* under the text in a narrow panel.
|
|
122
|
+
*/
|
|
123
|
+
@Component({
|
|
124
|
+
selector: 'app-example-timeline-audit',
|
|
125
|
+
imports: [BfButtonComponent, BfTimelineComponent, BfTimelineEventComponent],
|
|
126
|
+
template: `
|
|
127
|
+
<div class="flex max-w-xl flex-col gap-3">
|
|
128
|
+
<bf-timeline label="Audit trail">
|
|
129
|
+
@for (entry of entries; track entry.id) {
|
|
130
|
+
<bf-timeline-event [actor]="entry.actor" [time]="entry.time" [timeText]="entry.ago">
|
|
131
|
+
{{ entry.action }}
|
|
132
|
+
@if (entry.diff) {
|
|
133
|
+
<bf-button bfSlot="end" variant="outline" size="sm" (clicked)="showDiff(entry)">
|
|
134
|
+
Show diff
|
|
135
|
+
</bf-button>
|
|
136
|
+
}
|
|
137
|
+
</bf-timeline-event>
|
|
138
|
+
}
|
|
139
|
+
</bf-timeline>
|
|
140
|
+
<p data-status aria-live="polite" class="text-caption text-text-muted">{{ status() }}</p>
|
|
141
|
+
</div>
|
|
142
|
+
`,
|
|
143
|
+
})
|
|
144
|
+
export class TimelineAuditExampleComponent {
|
|
145
|
+
readonly entries = ENTRIES;
|
|
146
|
+
readonly status = signal('No diff open.');
|
|
147
|
+
|
|
148
|
+
/** A real app would open the change in a side panel. */
|
|
149
|
+
showDiff(entry: AuditEntry): void {
|
|
150
|
+
this.status.set(`Diff of "${entry.action}" by ${entry.actor}.`);
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Desktop
|
|
156
|
+
|
|
157
|
+
The timeline itself is not interactive: no hover, no pointer cursor, no focus stop. Only the links in the event text and the controls in an `end` slot take focus, with their own hover and focus ring. Its spacing does not follow `provideBfDefaults()`: the events keep one rhythm at every density, and it has no `size`. Inside `provideBfShell()` it renders and behaves the same; the `end` slot's buttons bring their own 44 px touch height.
|
|
158
|
+
|
|
159
|
+
## Theming
|
|
160
|
+
|
|
161
|
+
- Marker: a 10 px dot (`rounded-control`) in `text-subtle` (neutral), `primary`, `success`, `warning`, `danger` or `info`; `CanvasText` under forced colours.
|
|
162
|
+
- Line: a 1 px `border` line from marker to marker.
|
|
163
|
+
- Text: `text-body` in `text`; links `primary-text`, underlined, `ring` on keyboard focus. Meta line: `text-caption` in `text-muted`.
|
|
164
|
+
- Spacing: 12 px between the marker and the text, 20 px between events.
|
|
165
|
+
|
|
166
|
+
## Accessibility
|
|
167
|
+
|
|
168
|
+
The timeline is a list (`role="list"`, named by `label` through `aria-label`) and each event inside it a `listitem`, so a screen reader announces the number of events and the position of each. An event outside a `bf-timeline` claims no role. The marker and the line are `aria-hidden`; the tone is decoration, so the text must say what happened. The time is a `time` element whose `datetime` is the ISO value, and the separator between actor and time is hidden from assistive technology. Links and `end` slot controls are native elements in reading order.
|
|
169
|
+
|
|
170
|
+
## Inputs
|
|
171
|
+
|
|
172
|
+
| Name | Type | Default | Description |
|
|
173
|
+
| ------------ | ---------------------------- | ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
174
|
+
| `label` | `string` | `''` | The list's accessible name, such as "History"; empty leaves the list unnamed (a visible heading above it already names the section). |
|
|
175
|
+
| `timeFormat` | `Intl.DateTimeFormatOptions` | `TIMELINE_TIME_FORMAT` | `Intl.DateTimeFormat` options of every event's time; by default a medium date and a short time (`dateStyle: 'medium'`, `timeStyle: 'short'`). |
|
|
176
|
+
| `locale` | `string` | `''` | BCP 47 locale of the times, such as `de-CH`; empty follows the app's `LOCALE_ID`. |
|
|
177
|
+
|
|
178
|
+
## Slots
|
|
179
|
+
|
|
180
|
+
- `default`: The events, each a `bf-timeline-event`, in the order to show them.
|
|
181
|
+
|
|
182
|
+
## Notes
|
|
183
|
+
|
|
184
|
+
### Do
|
|
185
|
+
|
|
186
|
+
- Give the timeline a `label` when no visible heading names it, and keep one order (newest first) across the app.
|
|
187
|
+
- Name the actor and the object in every event; link the object.
|
|
188
|
+
|
|
189
|
+
### Don't
|
|
190
|
+
|
|
191
|
+
- Don't use a timeline for navigation or for steps the user still has to take.
|
|
192
|
+
- Don't carry meaning in the tone alone; say "CI failed", not just a red dot.
|
|
193
|
+
|
|
194
|
+
## Migration notes
|
|
195
|
+
|
|
196
|
+
### 0.4.0
|
|
197
|
+
|
|
198
|
+
New: `bf-timeline`, a dated history of events (who did what, and when) for a record's history
|
|
199
|
+
or an audit trail, in both shells. A `role="list"` named by `label`, in the order the consumer
|
|
200
|
+
gives (it never sorts); each event's time is formatted with `Intl.DateTimeFormat` in the app's
|
|
201
|
+
`LOCALE_ID` (`timeFormat`, default a medium date and a short time; `locale` overrides it).
|
package/ai/components/toggle.md
CHANGED
|
@@ -130,9 +130,11 @@ export class ToggleListRowExampleComponent {
|
|
|
130
130
|
}
|
|
131
131
|
```
|
|
132
132
|
|
|
133
|
+
In a table of switches (a settings matrix: alert kinds by channel), each cell's toggle is named after its column and row with `ariaLabel`; see [bf-table](https://bitforge-ui.web.app/components/table)'s Toggle cells.
|
|
134
|
+
|
|
133
135
|
## Sizes
|
|
134
136
|
|
|
135
|
-
`sm` (32 px row, 24×40 track), `md` (default, 40 px row, 28×48 track), `lg` (48 px row, 32×56 track). Unset, `size` follows `provideBfDefaults()`'s `controlSize`, `md` by default (`provideBfDefaults({ size: 'lg' })` enlarges every control of a Capacitor app; `{ size: 'sm' }` in a component's `providers` makes a dense sub-tree). Under a fine pointer the row is the hit area (32 px and up, above the 24 px of WCAG 2.5.8); under a coarse pointer (touch) `sm` and `md` rows grow to 44 px. The knob travels to the inline end, so it moves left under `dir="rtl"`.
|
|
137
|
+
`sm` (32 px row, 24×40 track), `md` (default, 40 px row, 28×48 track), `lg` (48 px row, 32×56 track). Unset, `size` follows `provideBfDefaults()`'s `controlSize`, `md` by default (`provideBfDefaults({ size: 'lg' })` enlarges every control of a Capacitor app; `{ size: 'sm' }` in a component's `providers` makes a dense sub-tree). Under a fine pointer the row is the hit area (32 px and up, above the 24 px of WCAG 2.5.8); under a coarse pointer (touch) `sm` and `md` rows grow to 44 px, and a label-less `sm` switch (40 px wide) also takes a 44 px width, its track centred. The knob travels to the inline end, so it moves left under `dir="rtl"`.
|
|
136
138
|
|
|
137
139
|
```ts
|
|
138
140
|
import { Component, signal } from '@angular/core';
|
|
@@ -325,6 +327,14 @@ Focuses the native switch; Signal Forms calls it from `focusBoundControl()`.
|
|
|
325
327
|
|
|
326
328
|
## Migration notes
|
|
327
329
|
|
|
330
|
+
### 0.4.0
|
|
331
|
+
|
|
332
|
+
A label-less `sm` toggle (a table cell) takes a 44 px min-width under a coarse pointer, its track
|
|
333
|
+
centred, so its touch target is 44 × 44 px (it was 40 px wide). Nothing changes under a fine
|
|
334
|
+
pointer or for `md` and `lg`. `@bitforgehq/ui/testing`: new `BfToggleHarness` (`with({ label })`
|
|
335
|
+
by the switch's computed name, `getLabel()`, `isChecked()`, `isDisabled()`, `isReadonly()`,
|
|
336
|
+
`toggle()`, `check()`, `uncheck()`, `getErrorText()`).
|
|
337
|
+
|
|
328
338
|
### 0.3.0
|
|
329
339
|
|
|
330
340
|
New `ariaLabelledby` input: the id of the element that names the switch when no visible `label`
|
package/ai/components/toolbar.md
CHANGED
|
@@ -12,11 +12,10 @@ import {
|
|
|
12
12
|
BfButtonComponent,
|
|
13
13
|
BfInputComponent,
|
|
14
14
|
BfSegmentedComponent,
|
|
15
|
-
BfSelectComponent,
|
|
16
15
|
BfToolbarComponent,
|
|
17
16
|
type BfSegmentOption,
|
|
18
|
-
type BfSelectOption,
|
|
19
17
|
} from '@bitforgehq/ui';
|
|
18
|
+
import { BfSelectComponent, type BfSelectOption } from '@bitforgehq/ui/overlay';
|
|
20
19
|
|
|
21
20
|
type ProjectStatus = 'active' | 'paused' | 'done';
|
|
22
21
|
type StatusFilter = ProjectStatus | 'all';
|
|
@@ -125,7 +124,7 @@ export class ToolbarOverviewExampleComponent {
|
|
|
125
124
|
Row of search, filters, a view switch and trailing actions above a list or table. Every
|
|
126
125
|
control projected into it is `sm` unless it sets `size` itself.
|
|
127
126
|
|
|
128
|
-
A dense row above a list or table: the search field and filters, a view switch, a live result count and the trailing actions. `bf-toolbar` lays the row out, wraps it in a narrow column and makes every control in it `sm`; the controls themselves are ordinary `bf-input`, `bf-select
|
|
127
|
+
A dense row above a list or table: the search field and filters, a view switch, a live result count and the trailing actions. `bf-toolbar` lays the row out, wraps it in a narrow column and makes every control in it `sm`; the controls themselves are ordinary `bf-input`, `bf-select` (from `@bitforgehq/ui/overlay`), `bf-chip-group`, `bf-segmented` and `bf-button`s.
|
|
129
128
|
|
|
130
129
|
## When to use
|
|
131
130
|
|
|
@@ -191,13 +190,8 @@ The row is a wrapping flex row with an 8 px gap between items and between rows.
|
|
|
191
190
|
|
|
192
191
|
```ts
|
|
193
192
|
import { Component } from '@angular/core';
|
|
194
|
-
import {
|
|
195
|
-
|
|
196
|
-
BfInputComponent,
|
|
197
|
-
BfSelectComponent,
|
|
198
|
-
BfToolbarComponent,
|
|
199
|
-
type BfSelectOption,
|
|
200
|
-
} from '@bitforgehq/ui';
|
|
193
|
+
import { BfButtonComponent, BfInputComponent, BfToolbarComponent } from '@bitforgehq/ui';
|
|
194
|
+
import { BfSelectComponent, type BfSelectOption } from '@bitforgehq/ui/overlay';
|
|
201
195
|
|
|
202
196
|
/**
|
|
203
197
|
* A toolbar that wraps.
|
|
@@ -238,13 +232,8 @@ At phone width (in a desktop web app's narrow window or a narrow column) the sea
|
|
|
238
232
|
|
|
239
233
|
```ts
|
|
240
234
|
import { Component, signal } from '@angular/core';
|
|
241
|
-
import {
|
|
242
|
-
|
|
243
|
-
BfInputComponent,
|
|
244
|
-
BfSelectComponent,
|
|
245
|
-
BfToolbarComponent,
|
|
246
|
-
type BfSelectOption,
|
|
247
|
-
} from '@bitforgehq/ui';
|
|
235
|
+
import { BfButtonComponent, BfInputComponent, BfToolbarComponent } from '@bitforgehq/ui';
|
|
236
|
+
import { BfSelectComponent, type BfSelectOption } from '@bitforgehq/ui/overlay';
|
|
248
237
|
|
|
249
238
|
/**
|
|
250
239
|
* A toolbar at phone width.
|
|
@@ -317,7 +306,7 @@ The `status` element is `role="status"` (an `aria-live="polite"` region), always
|
|
|
317
306
|
|
|
318
307
|
## Slots
|
|
319
308
|
|
|
320
|
-
- `default`: The search field and the filters (bf-input, bf-
|
|
309
|
+
- `default`: The search field and the filters (bf-input, bf-chip-group, bf-select from /overlay), in order.
|
|
321
310
|
- `view`: A view switch after the filters, such as a bf-segmented of list and grid.
|
|
322
311
|
- `actions`: The trailing actions, kept together at the inline end of the row, beside the status.
|
|
323
312
|
|
|
@@ -12,7 +12,9 @@ exposed: the heading stays a heading and the accessible name is the title alone.
|
|
|
12
12
|
with the nearest card or row, which then draws the pointer, the hover and, while the action has
|
|
13
13
|
keyboard focus, the focus ring; a card is never lit by the actions of rows nested in it. Give
|
|
14
14
|
secondary controls inside the surface `class="relative z-1"` so they stay clickable above the
|
|
15
|
-
stretched area.
|
|
15
|
+
stretched area. When the surface marks a state that belongs in the name (an `unread` list
|
|
16
|
+
row), the directive names the element `aria-labelledby="<state id> <own id>"`, so the link reads
|
|
17
|
+
"Unread, Account", and restores the element's own `aria-labelledby` when the state goes.
|
|
16
18
|
|
|
17
19
|
```ts
|
|
18
20
|
import { BfActionDirective } from '@bitforgehq/ui';
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# BfBadgeEmphasis
|
|
2
|
+
|
|
3
|
+
The two badge emphases, the soft tint or the solid strong fill
|
|
4
|
+
|
|
5
|
+
Type from `@bitforgehq/ui` · since 0.4.0
|
|
6
|
+
|
|
7
|
+
How loud a `bf-badge` is: `soft`, a pale tint with tinted text (the default), or `strong`, a
|
|
8
|
+
solid fill with contrasting text for the one state that must stand out, such as "Critical".
|
|
9
|
+
|
|
10
|
+
```ts
|
|
11
|
+
import { type BfBadgeEmphasis } from '@bitforgehq/ui';
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Signature
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
type BfBadgeEmphasis = 'soft' | 'strong'
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Values
|
|
21
|
+
|
|
22
|
+
- `soft`
|
|
23
|
+
- `strong`
|
|
24
|
+
|
|
25
|
+
## Used by
|
|
26
|
+
|
|
27
|
+
- [bf-badge](../components/badge.md)
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# BfChatComposerHarness
|
|
2
|
+
|
|
3
|
+
Test harness for the chat composer: types, sends, stops and reads its state
|
|
4
|
+
|
|
5
|
+
Class from `@bitforgehq/ui/testing` · since 0.5.0
|
|
6
|
+
|
|
7
|
+
Harness for `bf-chat-composer` (`@bitforgehq/ui/overlay`). Its `label` filter matches the text
|
|
8
|
+
field's name: the visible label, else its `aria-label` ("Message" by default).
|
|
9
|
+
|
|
10
|
+
```ts
|
|
11
|
+
import { BfChatComposerHarness } from '@bitforgehq/ui/testing';
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Signature
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
class BfChatComposerHarness extends ComponentHarness
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Methods
|
|
21
|
+
|
|
22
|
+
### `getLabel(): Promise<string>`
|
|
23
|
+
|
|
24
|
+
The text field's name: the visible label, else its `aria-label`.
|
|
25
|
+
|
|
26
|
+
### `getValue(): Promise<string>`
|
|
27
|
+
|
|
28
|
+
The text in the field.
|
|
29
|
+
|
|
30
|
+
### `setValue(text: string): Promise<void>`
|
|
31
|
+
|
|
32
|
+
Replaces the text as the user would type it (`changed` and the value update).
|
|
33
|
+
|
|
34
|
+
| Parameter | Type | Description |
|
|
35
|
+
| --------- | -------- | ------------- |
|
|
36
|
+
| `text` | `string` | The new text. |
|
|
37
|
+
|
|
38
|
+
### `send(): Promise<void>`
|
|
39
|
+
|
|
40
|
+
Sends the text with the send button, as a click does. Does nothing while it cannot send.
|
|
41
|
+
|
|
42
|
+
### `pressEnter(): Promise<void>`
|
|
43
|
+
|
|
44
|
+
Sends the text with `Enter` in the field, as the keyboard does.
|
|
45
|
+
|
|
46
|
+
### `stop(): Promise<void>`
|
|
47
|
+
|
|
48
|
+
Stops the running reply with the stop button. Does nothing unless a reply is running.
|
|
49
|
+
|
|
50
|
+
### `isRunning(): Promise<boolean>`
|
|
51
|
+
|
|
52
|
+
Whether a reply is running, so the button stops instead of sending.
|
|
53
|
+
|
|
54
|
+
### `isDisabled(): Promise<boolean>`
|
|
55
|
+
|
|
56
|
+
Whether the composer is disabled (its text field is).
|
|
57
|
+
|
|
58
|
+
### `canAct(): Promise<boolean>`
|
|
59
|
+
|
|
60
|
+
Whether the button can send (or stop) now: not disabled and, to send, with text.
|
|
61
|
+
|
|
62
|
+
### `getButtonLabel(): Promise<string>`
|
|
63
|
+
|
|
64
|
+
The button's name, "Send" or "Stop" by default.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# BfChatMessageRole
|
|
2
|
+
|
|
3
|
+
Who wrote a chat message, the user or the assistant
|
|
4
|
+
|
|
5
|
+
Type from `@bitforgehq/ui` · since 0.5.0
|
|
6
|
+
|
|
7
|
+
Who wrote a `bf-chat-message`: `user` (a tinted bubble at the inline end) or `assistant` (full
|
|
8
|
+
width, no bubble).
|
|
9
|
+
|
|
10
|
+
```ts
|
|
11
|
+
import { type BfChatMessageRole } from '@bitforgehq/ui';
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Signature
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
type BfChatMessageRole = 'user' | 'assistant'
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Values
|
|
21
|
+
|
|
22
|
+
- `user`
|
|
23
|
+
- `assistant`
|
|
24
|
+
|
|
25
|
+
## Used by
|
|
26
|
+
|
|
27
|
+
- [bf-chat-message](../components/chat-message.md)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# BfChatMessageStatus
|
|
2
|
+
|
|
3
|
+
How a chat message ended: done, stopped by the user, or failed
|
|
4
|
+
|
|
5
|
+
Type from `@bitforgehq/ui` · since 0.5.0
|
|
6
|
+
|
|
7
|
+
How a `bf-chat-message` ended: `done` (the default, no status line), `stopped` (the user
|
|
8
|
+
stopped the reply) or `failed` (it did not arrive or was not sent).
|
|
9
|
+
|
|
10
|
+
```ts
|
|
11
|
+
import { type BfChatMessageStatus } from '@bitforgehq/ui';
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Signature
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
type BfChatMessageStatus = 'done' | 'stopped' | 'failed'
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Values
|
|
21
|
+
|
|
22
|
+
- `done`
|
|
23
|
+
- `stopped`
|
|
24
|
+
- `failed`
|
|
25
|
+
|
|
26
|
+
## Used by
|
|
27
|
+
|
|
28
|
+
- [bf-chat-message](../components/chat-message.md)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# BfFadeInDirective
|
|
2
|
+
|
|
3
|
+
Fades an image in once it has loaded instead of popping in
|
|
4
|
+
|
|
5
|
+
Directive from `@bitforgehq/ui` · since 0.5.0
|
|
6
|
+
|
|
7
|
+
Fades an image in when it has loaded instead of letting it pop in line by line: put
|
|
8
|
+
`bfFadeIn` on the `<img>`. The image stays transparent until its `load` (or `error`) event,
|
|
9
|
+
then fades in over 260 ms (`duration-slow`), at once under `prefers-reduced-motion: reduce`. An
|
|
10
|
+
image that is already loaded when it renders (from the cache) shows straight away. Reserve its
|
|
11
|
+
space (`width`/`height` attributes or an aspect ratio) so the layout does not move when it
|
|
12
|
+
arrives; for a placeholder while it loads, put a `bf-skeleton` behind it.
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
import { BfFadeInDirective } from '@bitforgehq/ui';
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Selector: `img[bfFadeIn]`
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# BfFileInputHarness
|
|
2
|
+
|
|
3
|
+
Test harness for the file input: drops files and reads the list and rejections
|
|
4
|
+
|
|
5
|
+
Class from `@bitforgehq/ui/testing` · since 0.5.0
|
|
6
|
+
|
|
7
|
+
Harness for `bf-file-input`, in both looks. `addFiles()` drops files on the field, so they go
|
|
8
|
+
through the same `accept`, `maxSize` and `maxFiles` rules as a real drop; read the outcome with
|
|
9
|
+
`getFileNames()` and `getRejected()`. It works by file name, so a test never needs the rows.
|
|
10
|
+
|
|
11
|
+
```ts
|
|
12
|
+
import { BfFileInputHarness } from '@bitforgehq/ui/testing';
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Signature
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
class BfFileInputHarness extends ComponentHarness
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Methods
|
|
22
|
+
|
|
23
|
+
### `getLabel(): Promise<string>`
|
|
24
|
+
|
|
25
|
+
The visible label without the required mark, else `ariaLabel`.
|
|
26
|
+
|
|
27
|
+
### `getErrorText(): Promise<string>`
|
|
28
|
+
|
|
29
|
+
The error text under the field (an explicit message, or the first form error once touched).
|
|
30
|
+
|
|
31
|
+
### `getVariant(): Promise<'field' | 'dropzone'>`
|
|
32
|
+
|
|
33
|
+
The look of the field.
|
|
34
|
+
|
|
35
|
+
### `isDisabled(): Promise<boolean>`
|
|
36
|
+
|
|
37
|
+
Whether the field is disabled.
|
|
38
|
+
|
|
39
|
+
### `isReadonly(): Promise<boolean>`
|
|
40
|
+
|
|
41
|
+
Whether the field is read-only: it has no pick target.
|
|
42
|
+
|
|
43
|
+
### `addFiles(files: File[]): Promise<void>`
|
|
44
|
+
|
|
45
|
+
Drops files on the field, as a user would. On a disabled or read-only field it does nothing,
|
|
46
|
+
like the UI.
|
|
47
|
+
|
|
48
|
+
| Parameter | Type | Description |
|
|
49
|
+
| --------- | -------- | ------------------ |
|
|
50
|
+
| `files` | `File[]` | The files to drop. |
|
|
51
|
+
|
|
52
|
+
### `getFileNames(): Promise<string[]>`
|
|
53
|
+
|
|
54
|
+
The names of the chosen files, in order (rejected files are not included).
|
|
55
|
+
|
|
56
|
+
### `getRejected(): Promise<{ name: string; message: string }[]>`
|
|
57
|
+
|
|
58
|
+
The files the last pick or drop refused, each with its reason, until they are dismissed.
|
|
59
|
+
|
|
60
|
+
### `removeFile(name: string): Promise<void>`
|
|
61
|
+
|
|
62
|
+
Removes the chosen file with this name, as the row's remove button does. Throws when no
|
|
63
|
+
chosen file has that name or the field cannot be edited.
|
|
64
|
+
|
|
65
|
+
| Parameter | Type | Description |
|
|
66
|
+
| --------- | -------- | ---------------- |
|
|
67
|
+
| `name` | `string` | The file's name. |
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# BfFileInputVariant
|
|
2
|
+
|
|
3
|
+
The two looks of a file picker, a compact field or a drop zone
|
|
4
|
+
|
|
5
|
+
Type from `@bitforgehq/ui` · since 0.5.0
|
|
6
|
+
|
|
7
|
+
The look of a `bf-file-input`: a compact field with a pick button (`field`), or a drop zone
|
|
8
|
+
(`dropzone`). Both have the same API and behaviour.
|
|
9
|
+
|
|
10
|
+
```ts
|
|
11
|
+
import { type BfFileInputVariant } from '@bitforgehq/ui';
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Signature
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
type BfFileInputVariant = 'field' | 'dropzone'
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Values
|
|
21
|
+
|
|
22
|
+
- `field`
|
|
23
|
+
- `dropzone`
|
|
24
|
+
|
|
25
|
+
## Used by
|
|
26
|
+
|
|
27
|
+
- [bf-file-input](../components/file-input.md)
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# BfImageInputHarness
|
|
2
|
+
|
|
3
|
+
Test harness for the image input: drops an image on it and reads the preview
|
|
4
|
+
|
|
5
|
+
Class from `@bitforgehq/ui/testing` · since 0.5.0
|
|
6
|
+
|
|
7
|
+
Harness for `bf-image-input`. `addFile()` drops the image on the tile, so it goes through the
|
|
8
|
+
same `accept` and `maxSize` rules as a real drop; read the outcome with `hasPreview()`,
|
|
9
|
+
`getPreviewSrc()` and `getRejectedMessage()`. jsdom has no `URL.createObjectURL`, so a picked
|
|
10
|
+
image shows no preview there: assert on the bound value, or stub `URL.createObjectURL` to read
|
|
11
|
+
the preview.
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { BfImageInputHarness } from '@bitforgehq/ui/testing';
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## Signature
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
class BfImageInputHarness extends ComponentHarness
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Methods
|
|
24
|
+
|
|
25
|
+
### `getLabel(): Promise<string>`
|
|
26
|
+
|
|
27
|
+
The visible label without the required mark, else the tile's `aria-label`.
|
|
28
|
+
|
|
29
|
+
### `getErrorText(): Promise<string>`
|
|
30
|
+
|
|
31
|
+
The error text beside the tile (an explicit message, or the first form error once touched).
|
|
32
|
+
|
|
33
|
+
### `isDisabled(): Promise<boolean>`
|
|
34
|
+
|
|
35
|
+
Whether the control is disabled.
|
|
36
|
+
|
|
37
|
+
### `isReadonly(): Promise<boolean>`
|
|
38
|
+
|
|
39
|
+
Whether the control is read-only: its tile is not a button and there are no Upload or Remove
|
|
40
|
+
buttons.
|
|
41
|
+
|
|
42
|
+
### `hasPreview(): Promise<boolean>`
|
|
43
|
+
|
|
44
|
+
Whether the tile shows an image, picked or from `src`. A picked image needs
|
|
45
|
+
`URL.createObjectURL`, which jsdom lacks: stub it to see one.
|
|
46
|
+
|
|
47
|
+
### `getPreviewSrc(): Promise<string | null>`
|
|
48
|
+
|
|
49
|
+
The `src` of the shown image.
|
|
50
|
+
|
|
51
|
+
### `addFile(file: File): Promise<void>`
|
|
52
|
+
|
|
53
|
+
Drops an image on the tile, as a user would. On a disabled or read-only control it does
|
|
54
|
+
nothing, like the UI. It waits on no timer, so it works under fake timers and `fakeAsync`.
|
|
55
|
+
|
|
56
|
+
| Parameter | Type | Description |
|
|
57
|
+
| --------- | ------ | ------------------ |
|
|
58
|
+
| `file` | `File` | The image to drop. |
|
|
59
|
+
|
|
60
|
+
### `remove(): Promise<void>`
|
|
61
|
+
|
|
62
|
+
Presses Remove, which clears the picked and the existing image. Throws while there is no
|
|
63
|
+
image or the control cannot be edited.
|
|
64
|
+
|
|
65
|
+
### `getRejectedMessage(): Promise<string>`
|
|
66
|
+
|
|
67
|
+
The reason the last pick was refused, until the next pick or Remove.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# BfImageInputShape
|
|
2
|
+
|
|
3
|
+
The two tile shapes of an image picker, a circle or a rounded square
|
|
4
|
+
|
|
5
|
+
Type from `@bitforgehq/ui` · since 0.5.0
|
|
6
|
+
|
|
7
|
+
The shape of a `bf-image-input` tile: a circle (`round`, for avatars) or a rounded square
|
|
8
|
+
(`square`, for logos and covers).
|
|
9
|
+
|
|
10
|
+
```ts
|
|
11
|
+
import { type BfImageInputShape } from '@bitforgehq/ui';
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Signature
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
type BfImageInputShape = 'round' | 'square'
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Values
|
|
21
|
+
|
|
22
|
+
- `round`
|
|
23
|
+
- `square`
|
|
24
|
+
|
|
25
|
+
## Used by
|
|
26
|
+
|
|
27
|
+
- [bf-image-input](../components/image-input.md)
|