@bitforgehq/ui 0.4.0 → 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 +303 -0
- package/README.md +12 -12
- package/ai/MANIFEST.index.json +65 -2
- package/ai/MANIFEST.json +2221 -737
- package/ai/REFERENCE.index.json +161 -0
- package/ai/REFERENCE.json +1336 -27
- package/ai/UI_GUIDE.md +61 -20
- package/ai/components/app-frame.md +1 -1
- 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/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/tab-group.md +18 -2
- package/ai/components/table.md +17 -2
- package/ai/components/theme-menu-button.md +1 -0
- package/ai/components/time-input.md +2 -0
- package/ai/components/toolbar.md +7 -18
- 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 -75
- 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_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/fesm2022/bitforgehq-ui-overlay.mjs +2649 -606
- package/fesm2022/bitforgehq-ui-overlay.mjs.map +1 -1
- package/fesm2022/bitforgehq-ui-testing.mjs +356 -10
- package/fesm2022/bitforgehq-ui-testing.mjs.map +1 -1
- package/fesm2022/bitforgehq-ui.mjs +7531 -3989
- package/fesm2022/bitforgehq-ui.mjs.map +1 -1
- package/llms-full.txt +4151 -130
- package/llms.txt +31 -1
- package/package.json +1 -1
- package/theme.css +60 -0
- package/types/bitforgehq-ui-overlay.d.ts +684 -7
- package/types/bitforgehq-ui-testing.d.ts +434 -6
- package/types/bitforgehq-ui.d.ts +1473 -182
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,309 @@ All notable changes to `@bitforgehq/ui`. One `##` section per version; one `###`
|
|
|
4
4
|
|
|
5
5
|
The heading for the version being worked on carries `(unreleased)` in place of a date. At release time, that placeholder is replaced with the release date (`## 0.3.1 (unreleased)` becomes `## 0.3.1 (YYYY-MM-DD)`) in the same commit that bumps `projects/ui/package.json`. See "Releasing" in the root `README.md`. 0.3.0 is the first version published to npm; the 0.2.0 and 0.1.0 sections below are the history of versions that were never published.
|
|
6
6
|
|
|
7
|
+
## 0.5.0 (unreleased)
|
|
8
|
+
|
|
9
|
+
### Migration
|
|
10
|
+
|
|
11
|
+
Breaking changes from 0.4.x, each with its fix:
|
|
12
|
+
|
|
13
|
+
1. `bf-select` moved to `@bitforgehq/ui/overlay`. Before:
|
|
14
|
+
`import { BfSelectComponent } from '@bitforgehq/ui'`. After:
|
|
15
|
+
`import { BfSelectComponent } from '@bitforgehq/ui/overlay'`, which needs `@angular/cdk` and
|
|
16
|
+
`rxjs` (`bun add @angular/cdk rxjs`). The selector, inputs, outputs and Signal Forms binding
|
|
17
|
+
are unchanged; `BfSelectOption` is still exported from `@bitforgehq/ui` (and now from
|
|
18
|
+
`/overlay` too).
|
|
19
|
+
2. Under a mouse `bf-select` is no longer a native `<select>`. `checkValidity()` and the
|
|
20
|
+
browser's "required" popup do not apply in the styled mode; enforce `required` through Signal
|
|
21
|
+
Forms. Tests that set a `<select>`'s value directly pick by label with `BfSelectHarness`
|
|
22
|
+
instead, which drives both modes.
|
|
23
|
+
3. A stacked `bf-table`'s "Sort by" control stays the native select unless the app adds
|
|
24
|
+
`provideBfSelect()` from `@bitforgehq/ui/overlay` to its providers. Without it, that
|
|
25
|
+
control's host element changed from `bf-select` to `bf-native-select[data-bf-select]`: update
|
|
26
|
+
app CSS and DOM queries such as `bf-table bf-select`.
|
|
27
|
+
4. A collapsed `bf-sidebar` is a fixed rail (56, 64 or 72 px for `controlSize` `sm`, `md` and
|
|
28
|
+
`lg`; 68 px under a coarse pointer for `sm` and `md`) instead of `w-auto`, so its width can
|
|
29
|
+
animate. Its regions no longer centre their content while collapsed. Before: header or footer
|
|
30
|
+
content wider than the icons widened the rail, and narrower content was centred. After: the
|
|
31
|
+
rail keeps its width and clips wider content, and content is aligned to the inline start. Fix:
|
|
32
|
+
make collapsed header and footer content fit the rail, using `in-data-collapsed:` to show a
|
|
33
|
+
compact brand and to fade or close the rest (see the sidebar's Motion section), and put
|
|
34
|
+
stacked icon buttons in a `flex flex-col items-start` column.
|
|
35
|
+
|
|
36
|
+
### file-input
|
|
37
|
+
|
|
38
|
+
New: `bf-file-input`, a file picker field. The `field` look (the default) is a tonal "Choose
|
|
39
|
+
file" button inside a field box that also takes dropped files; `variant="dropzone"` is a dashed
|
|
40
|
+
area that is the control as a whole (click, Enter, Space or a drop). The chosen files are listed
|
|
41
|
+
under it with a thumbnail (images) or the extension, the name, the size and a remove button. The
|
|
42
|
+
value is a `File[]` (one file at most without `multiple`), bound with `[(value)]` or
|
|
43
|
+
`[formField]`. `accept` (MIME types, `type/*` and `.ext`, checked on drops too), `maxSize`
|
|
44
|
+
(bytes) and `maxFiles` decide what is added; a file that breaks one never enters the value and
|
|
45
|
+
never sets `invalid`: it shows as a red row with the reason until the next pick and is emitted
|
|
46
|
+
through `rejected` (new types `BfRejectedFile`: `file`, `reason`, `message`; and
|
|
47
|
+
`BfRejectedFileReason`: `'type' | 'size' | 'count'`). It only picks; uploading stays with the app. New type `BfFileInputVariant`, new label
|
|
48
|
+
keys `fileInputChoose`, `fileInputDropHint`, `fileInputDropzone`, `fileInputTapToChoose`,
|
|
49
|
+
`fileInputRemove`, `fileInputDismiss`, `fileInputAdded`, `fileInputNotAdded`,
|
|
50
|
+
`fileInputRemoved`, `fileInputCount`, `fileRejectedType`, `fileRejectedSize` and
|
|
51
|
+
`fileRejectedCount`. `@bitforgehq/ui/testing`: new `BfFileInputHarness` (`addFiles()` drops files
|
|
52
|
+
through the same rules, `getFileNames()`, `getRejected()`, `removeFile()`, `getVariant()`).
|
|
53
|
+
|
|
54
|
+
### image-input
|
|
55
|
+
|
|
56
|
+
New: `bf-image-input`, a single-image picker for an avatar, logo or cover: a `round` or `square`
|
|
57
|
+
tile (`shape`, new type `BfImageInputShape`; 64, 96 or 128 px with `size`) that shows the image or
|
|
58
|
+
a camera glyph, with Upload/Replace and Remove beside it. The value is a `File | null`. `src`, a
|
|
59
|
+
two-way `model()`, shows the image the app already has; Remove clears both `value` and `src`, so
|
|
60
|
+
the app can tell a new picture (`value` set), a removed one (both null) and an unchanged one
|
|
61
|
+
(`src` kept, `value` null) apart. `accept` (default `image/*`) and `maxSize` refuse an image
|
|
62
|
+
without touching the current one: the reason shows under the buttons and `rejected` emits it. It
|
|
63
|
+
only picks; uploading stays with the app. New label keys `imageInputChoose`, `imageInputUpload`,
|
|
64
|
+
`imageInputReplace` and `imageInputRemove`. `@bitforgehq/ui/testing`: new `BfImageInputHarness`
|
|
65
|
+
(`addFile()`, `hasPreview()`, `getPreviewSrc()`, `remove()`, `getRejectedMessage()`).
|
|
66
|
+
|
|
67
|
+
### select
|
|
68
|
+
|
|
69
|
+
**Breaking:** `bf-select` moved from `@bitforgehq/ui` to `@bitforgehq/ui/overlay` (see
|
|
70
|
+
Migration). Under a fine pointer it now opens a styled list, the combobox's panel without the
|
|
71
|
+
search, exactly as wide as the field: a focusable `role="combobox"` field following the APG
|
|
72
|
+
select-only combobox pattern (Enter, Space, the arrows and Alt+ArrowDown open; the arrows,
|
|
73
|
+
Home/End and PageUp/PageDown move; Enter, Space, Tab or a click pick; Escape closes without
|
|
74
|
+
closing a dialog underneath). Typeahead (500 ms buffer, case and accents ignored) moves the active
|
|
75
|
+
option, open or closed, and never commits on its own. Under a coarse pointer
|
|
76
|
+
(`matchMedia('(pointer: coarse)')`, followed live) it renders the native `<select>` as before;
|
|
77
|
+
on the server it renders the styled field. With `name` set, a hidden input carries the chosen
|
|
78
|
+
option's key, so plain form posts keep working (none while disabled). With a visible `label` the
|
|
79
|
+
field is named by `aria-labelledby` pointing at the label (a `<label for>` cannot name a div);
|
|
80
|
+
`ariaLabel` and `ariaLabelledby` still apply only without one. Under forced colours a disabled
|
|
81
|
+
field shows `GrayText`. `@bitforgehq/ui/testing`: `BfSelectHarness` drives both modes by label,
|
|
82
|
+
and has a new `isOpen()`; in both modes `getOptionLabels()` reads a read-only or disabled
|
|
83
|
+
select's options, and `selectOption()` on a read-only select changes nothing.
|
|
84
|
+
|
|
85
|
+
The styled list animates open and closed with the popover's motion (180 ms in, 130 ms out). The
|
|
86
|
+
field's `aria-expanded` and focus change at once; the closing list is inert and keeps the options
|
|
87
|
+
it showed until it is gone. Under `prefers-reduced-motion` it only fades. The native select under
|
|
88
|
+
touch is unchanged.
|
|
89
|
+
|
|
90
|
+
### field
|
|
91
|
+
|
|
92
|
+
New `labelId`: the label's id (`<controlId>-label`), which the label now carries, for a control
|
|
93
|
+
that names itself with `aria-labelledby`.
|
|
94
|
+
|
|
95
|
+
The error message fades in when it appears (new `--animate-message-in`, 150 ms of opacity),
|
|
96
|
+
including a message held back during a press, which fades in once the press ends. `bf-toggle` and
|
|
97
|
+
`bf-calendar` messages do the same. It is an opacity fade only, so it stays under reduced motion.
|
|
98
|
+
|
|
99
|
+
### overlay core
|
|
100
|
+
|
|
101
|
+
Anchored panels (combobox, select, popover, tooltip, date and time pickers), dialogs, side panels
|
|
102
|
+
and desktop toasts build their CDK overlays with the CDK's standalone factories
|
|
103
|
+
(`createOverlayRef()` and the `create*Strategy()` functions) instead of the `Overlay` service, so
|
|
104
|
+
an app's bundle no longer carries the scroll and position strategies it never uses.
|
|
105
|
+
|
|
106
|
+
### combobox
|
|
107
|
+
|
|
108
|
+
The list panel is exactly as wide as the field (it shrank to its content inside the CDK pane).
|
|
109
|
+
|
|
110
|
+
The list of `bf-combobox` and `bf-multi-combobox` animates open and closed with the popover's
|
|
111
|
+
motion (180 ms in, 130 ms out). `aria-expanded` changes at once; the closing list is inert and
|
|
112
|
+
keeps the options it showed, so a reset query does not reflow it while it fades, and typing or
|
|
113
|
+
`ArrowDown` during the exit opens a fresh list. Under `prefers-reduced-motion` it only fades.
|
|
114
|
+
|
|
115
|
+
### multi-combobox
|
|
116
|
+
|
|
117
|
+
The list panel is exactly as wide as the field. The inner text input no longer shows a square
|
|
118
|
+
border and ring on focus in apps that load `@tailwindcss/forms`.
|
|
119
|
+
|
|
120
|
+
### table
|
|
121
|
+
|
|
122
|
+
New `BF_TABLE_SORT_SELECT` injection token: the component a stacked table renders as its "Sort
|
|
123
|
+
by" select (`null`, the default, keeps the native one). `provideBfSelect()` from
|
|
124
|
+
`@bitforgehq/ui/overlay` sets it to the styled `bf-select`, so the core entry still never imports
|
|
125
|
+
the CDK. `BfTableHarness.getSortSelect()` returns a working `BfSelectHarness` either way.
|
|
126
|
+
|
|
127
|
+
### theme
|
|
128
|
+
|
|
129
|
+
New motion token `--bf-duration-layout` (200 ms) in `:root`, beside `--bf-duration-fast` and
|
|
130
|
+
`--bf-duration-overlay`: the timing of size and position changes (the sidebar's width, sliding
|
|
131
|
+
selection indicators). Use it with `ease-md`, as in
|
|
132
|
+
`transition-[width] duration-(--bf-duration-layout) ease-md motion-reduce:transition-none`.
|
|
133
|
+
|
|
134
|
+
### sidebar
|
|
135
|
+
|
|
136
|
+
Collapsing and expanding now animate: the width moves over `--bf-duration-layout` on `ease-md`
|
|
137
|
+
from `w-60` to a fixed rail (see Migration 4), and the content beside it (the `bf-app-frame`
|
|
138
|
+
column) moves with it. Under `prefers-reduced-motion: reduce` it still switches at once. The first
|
|
139
|
+
render never animates, so a collapsed state restored by `provideBfSidebarState()` does not slide
|
|
140
|
+
on load. The host carries `data-collapsed` as before; header and footer content can follow on the
|
|
141
|
+
same timing with `in-data-collapsed:` (new Motion section in the docs).
|
|
142
|
+
|
|
143
|
+
### nav-item
|
|
144
|
+
|
|
145
|
+
In a collapsing sidebar the item no longer switches to a centred square: the row keeps its layout
|
|
146
|
+
and its start padding moves so the icon ends centred in the rail without moving, while the label
|
|
147
|
+
fades out (it stays the accessible name; it is no longer `sr-only`). The count pill shrinks away
|
|
148
|
+
while the dot (new element, `[data-badge-dot]`) grows in on the same timing. Each item clips
|
|
149
|
+
what does not fit the rail. No animation under reduced motion or in a top nav.
|
|
150
|
+
|
|
151
|
+
### nav-group
|
|
152
|
+
|
|
153
|
+
Collapsed, the heading's row closes and its text fades out on `--bf-duration-layout` instead of
|
|
154
|
+
switching to `sr-only`; it stays in the accessibility tree and still names the group. The heading
|
|
155
|
+
stays on one line and truncates (it wrapped before), so its height does not change while the
|
|
156
|
+
width moves.
|
|
157
|
+
|
|
158
|
+
### checkbox
|
|
159
|
+
|
|
160
|
+
The check mark and the dash grow in as they appear and shrink as they go (150 ms on `ease-md`),
|
|
161
|
+
instead of switching opacity at once; under reduced motion they only fade. An error message fades
|
|
162
|
+
in when it appears.
|
|
163
|
+
|
|
164
|
+
### radio-group
|
|
165
|
+
|
|
166
|
+
The selected option's dot grows in and the previous one shrinks away (150 ms on `ease-md`);
|
|
167
|
+
under reduced motion they only fade. An error message fades in when it appears.
|
|
168
|
+
|
|
169
|
+
### popover
|
|
170
|
+
|
|
171
|
+
The panel animates open and closed. It grows out of the trigger (a fade, a scale from 96 % and a
|
|
172
|
+
4 px slide away from the trigger, 180 ms, ease-out; transform origin on the trigger's corner,
|
|
173
|
+
following a flip above or below) and shrinks back on close (130 ms). Focus, `aria-expanded` and
|
|
174
|
+
`bfPopoverClosed` still change at once; the closing panel stays in the overlay container, inert
|
|
175
|
+
and click-through, until its exit has played, and opening again during the exit replaces it. Under
|
|
176
|
+
`prefers-reduced-motion` it only fades. Where the Web Animations API is missing or stubbed
|
|
177
|
+
(jsdom), the panel is still removed at once.
|
|
178
|
+
|
|
179
|
+
### menu
|
|
180
|
+
|
|
181
|
+
The menu animates open: it grows out of its trigger (a fade, a scale from 96 % and a 4 px slide,
|
|
182
|
+
180 ms, ease-out), a submenu out of its item. It still closes at once, because the CDK menu
|
|
183
|
+
removes it immediately. Under `prefers-reduced-motion` it only fades in. `bf-menu-button` and
|
|
184
|
+
`bf-theme-menu-button` get the same enter.
|
|
185
|
+
|
|
186
|
+
### date-input
|
|
187
|
+
|
|
188
|
+
The calendar popover of `bf-date-input`, the time list of `bf-time-input` and the popover of
|
|
189
|
+
`bf-datetime-input` animate open and closed with the popover's motion (180 ms in, 130 ms out).
|
|
190
|
+
Focus returns to the button at once; the closing panel is inert until its exit has played. Under
|
|
191
|
+
`prefers-reduced-motion` they only fade.
|
|
192
|
+
|
|
193
|
+
### motion
|
|
194
|
+
|
|
195
|
+
New duration utilities in the theme: `duration-fast` (150 ms), `duration-base` (200 ms) and
|
|
196
|
+
`duration-slow` (260 ms), the timings the widgets use (`--transition-duration-*` reading
|
|
197
|
+
`--bf-duration-fast`, `--bf-duration-layout` and `--bf-duration-overlay`, so redefining those
|
|
198
|
+
retimes both). New keyframe utilities for app lists with Angular's `animate.enter` and
|
|
199
|
+
`animate.leave`: `animate-item-enter` (fades and rises in, 200 ms on `ease-settle`) and
|
|
200
|
+
`animate-item-leave` (fades out, 150 ms), used behind `motion-safe:`. New `bfMotionList`
|
|
201
|
+
directive (`BfMotionListDirective`): on a list or grid container it glides items to their new
|
|
202
|
+
place when items are added, removed or reordered (FLIP, 200 ms, none under reduced motion or on a
|
|
203
|
+
resize). New `img[bfFadeIn]` directive (`BfFadeInDirective`): the image stays transparent until it
|
|
204
|
+
loads or fails, then fades in over 260 ms; at once from the cache and under reduced motion. The
|
|
205
|
+
Theming guide has a new Motion section with a live example.
|
|
206
|
+
|
|
207
|
+
### tab-group
|
|
208
|
+
|
|
209
|
+
The selected tab's underline is now one indicator element of the row that slides to the newly
|
|
210
|
+
selected tab, changing width as it goes (`--bf-duration-layout`, `ease-md`). The first placement
|
|
211
|
+
and a resize land at once, and so does everything under `prefers-reduced-motion`. It is measured
|
|
212
|
+
from the selected tab's inline-start offset, so it mirrors under `dir="rtl"`, and is re-measured
|
|
213
|
+
on resize and when labels, badges, size or `fitted` change. Until it has been measured (jsdom,
|
|
214
|
+
server render, a hidden group) the tab keeps drawing its own underline, so unit tests and the
|
|
215
|
+
first paint are unchanged. The indicator is `aria-hidden`; roles, keyboard and focus are
|
|
216
|
+
unchanged.
|
|
217
|
+
|
|
218
|
+
The panel a user switches to fades in (200 ms of opacity, new `--animate-panel-in`, behind
|
|
219
|
+
`motion-safe:`); the panel shown on the first render never does.
|
|
220
|
+
|
|
221
|
+
### segmented
|
|
222
|
+
|
|
223
|
+
The selected segment's pill (surface fill, `shadow-segment`, `primary-strong` outline,
|
|
224
|
+
`Highlight` under forced colours) is now one indicator element of the track that slides to the
|
|
225
|
+
newly chosen segment, changing width as it goes (`--bf-duration-layout`, `ease-md`). The first
|
|
226
|
+
placement and a resize land at once, and so does everything under `prefers-reduced-motion`. The
|
|
227
|
+
selected segment's hover and pressed fills move with it. Until it has been measured (jsdom,
|
|
228
|
+
server render, a hidden control) the segment keeps painting its own pill. `bf-theme-switch`
|
|
229
|
+
slides the same way. The indicator is `aria-hidden`; roles, keyboard and focus are unchanged.
|
|
230
|
+
|
|
231
|
+
### chat-thread
|
|
232
|
+
|
|
233
|
+
New: `bf-chat-thread` and `bf-chat-message`, the message list of a chat with an assistant. The
|
|
234
|
+
thread is a scrolling `role="log"` (`aria-live="polite"`, named by `label` or the new
|
|
235
|
+
`chatThread` label) that stays pinned to the newest message while text arrives. When the user
|
|
236
|
+
scrolls up it stops following and shows a "Jump to latest" button (new label
|
|
237
|
+
`chatJumpToLatest`, optional `jump-icon` slot), which scrolls back down (smoothly, at once under
|
|
238
|
+
reduced motion) and follows again. While a message streams the log is `aria-busy`, so the reply
|
|
239
|
+
is announced once, when it is complete. Messages added after the first render rise 4 px and fade
|
|
240
|
+
in (new `--animate-chat-message-in`, 200 ms on `--ease-settle`, off under reduced motion). A
|
|
241
|
+
message's `role` (new type `BfChatMessageRole`: `'user' | 'assistant'`) draws a `primary-soft`
|
|
242
|
+
bubble at the inline end or a full-width reply; `author`, `time` and `timeText` fill an optional
|
|
243
|
+
header; `streaming` shows a pulsing caret at the end of the text (new `--animate-chat-caret`,
|
|
244
|
+
static under reduced motion); `status` (new type `BfChatMessageStatus`:
|
|
245
|
+
`'done' | 'stopped' | 'failed'`) adds a status line (new labels `chatMessageStopped`,
|
|
246
|
+
`chatMessageFailed`, or `statusMessage`) with an `action` slot for a Retry button; the `content`
|
|
247
|
+
slot holds cards, images or chips under the text. New directive `bfProse` (`BfProseDirective`)
|
|
248
|
+
styles the app's rendered markdown (headings, lists, links, code, quotes) with the semantic tokens;
|
|
249
|
+
the message body uses it.
|
|
250
|
+
|
|
251
|
+
### chat-composer
|
|
252
|
+
|
|
253
|
+
New: `bf-chat-composer` in `@bitforgehq/ui/overlay`, the input row of a chat. A `bf-textarea`
|
|
254
|
+
with `autoResize` grows from one line to `maxRows` (8), then scrolls. `Enter` sends and
|
|
255
|
+
`Shift+Enter` starts a new line; nothing sends during IME composition or with whitespace-only
|
|
256
|
+
text. `value` is a `model()` that clears after a send; `sent` emits the trimmed text, `changed`
|
|
257
|
+
each edit. While `running` the send button is a stop button that emits `stopped`. Icons go in the
|
|
258
|
+
`send-icon` and `stop-icon` slots (the text stays as the button's name, and shows without an
|
|
259
|
+
icon); `start` and `end` slots take extra controls. `disabled` with `disabledReason` shows the
|
|
260
|
+
reason as the button's tooltip (`aria-disabled`, still focusable) and the empty field's
|
|
261
|
+
placeholder. `size` follows `fieldSize`. New labels `chatComposer`, `chatSend` and `chatStop`.
|
|
262
|
+
`@bitforgehq/ui/testing`: new `BfChatComposerHarness` (`setValue()`, `getValue()`, `send()`,
|
|
263
|
+
`pressEnter()`, `stop()`, `isRunning()`, `isDisabled()`, `canAct()`, `getButtonLabel()`).
|
|
264
|
+
|
|
265
|
+
### media-card
|
|
266
|
+
|
|
267
|
+
New: `bf-media-card`, a card led by a picture (a game pitch, a generated image). The card renders
|
|
268
|
+
the picture itself from `src` and `alt` in a top area at a fixed `aspect` (new type
|
|
269
|
+
`BfMediaCardAspect`: `'16/9'` (default), `'4/3'`, `'1/1'`, `'3/4'`). While it loads, and while
|
|
270
|
+
`src` is empty but `loading` is set (a picture still being made), the area shows a `bf-skeleton`
|
|
271
|
+
shimmer with an optional `loadingLabel` caption ("Drawing…") and is `aria-busy`. The picture fades
|
|
272
|
+
in over 260 ms (`bfFadeIn`) while the skeleton fades out under it; at once under reduced motion.
|
|
273
|
+
A load error, or `failed` set by the app, shows `failedLabel` (default: the new label
|
|
274
|
+
`imageLoadFailed`) and the `bfSlot="failed-action"` content (Retry); a new `src` starts over.
|
|
275
|
+
`loaded` and `errored` report the image's events. A body slot and a `footer` slot follow. A
|
|
276
|
+
`bfAction` link or button in the title makes the whole card clickable, as on `bf-card` (pointer,
|
|
277
|
+
`shadow-floating` hover, focus ring, fading on `duration-fast`). `selectable` adds a toggle
|
|
278
|
+
button (`aria-pressed`) bound with `[(selected)]` (a `model()`), emitting `changed` on each user
|
|
279
|
+
change: the whole card without a `bfAction`, else a round check button in the image's corner; a
|
|
280
|
+
selected card has a 2 px primary ring and a filled check. `label` names it ("Select" + `label`,
|
|
281
|
+
new label `mediaCardSelect`). `@bitforgehq/ui/testing`: new `BfMediaCardHarness`
|
|
282
|
+
(`getState()`, `loadImage()`, `failImage()`, `getLoadingLabel()`, `getFailedMessage()`,
|
|
283
|
+
`isSelected()`, `toggleSelected()`).
|
|
284
|
+
|
|
285
|
+
### image-viewer
|
|
286
|
+
|
|
287
|
+
New: `BfImageViewerService` in `@bitforgehq/ui/overlay`, a full-screen image viewer (lightbox).
|
|
288
|
+
`open({ images: [{ src, label, caption? }], index?, origin?, actions?, ariaLabel?,
|
|
289
|
+
closeOnNavigation?, injector? })` (new types `BfImageViewerOptions`, `BfImageViewerImage`,
|
|
290
|
+
`BfImageViewerOrigin`, `BfImageViewerActionsContext`) renders `bf-image-viewer` over a dark scrim
|
|
291
|
+
and returns a `BfImageViewerRef` (`index()`, `next()`, `previous()`, `goTo()`, `close()`,
|
|
292
|
+
`afterClosed`, `closed()`). Previous and next buttons, the arrow keys (mirrored under rtl), Home
|
|
293
|
+
and End step through the set, wrapping at both ends; a counter shows "2 of 5" and a polite live
|
|
294
|
+
region announces each step. It is a modal dialog: focus moves to its close button and is trapped,
|
|
295
|
+
`Escape`, the close button and a click on the scrim close it, the page behind is `inert`, and
|
|
296
|
+
focus returns to the opener. The `actions` template (context `{ $implicit: image, index, ref }`)
|
|
297
|
+
sits beside the caption. It zooms out of `origin` (the clicked thumbnail, or a function returning
|
|
298
|
+
any image's thumbnail) with a 260 ms FLIP on the overlay curve and zooms back into it on close
|
|
299
|
+
when it is on screen, else fades; images cross-fade on a step; reduced motion only fades. While a
|
|
300
|
+
large image loads it shows the thumbnail and a spinner, then fades the image in. New labels
|
|
301
|
+
`imageViewer`, `imageViewerPrevious`, `imageViewerNext` and `imageViewerPosition`.
|
|
302
|
+
`@bitforgehq/ui/testing`: new `BfImageViewerHarness` (`getIndex()`, `getCount()`,
|
|
303
|
+
`getCounterText()`, `getSrc()`, `getAlt()`, `getCaption()`, `next()`, `previous()`,
|
|
304
|
+
`pressKey()`, `close()`, `loadImage()`, `failImage()`, `isLoaded()`, `hasFailed()`).
|
|
305
|
+
|
|
306
|
+
## 0.5.0-next.3 (2026-11-07)
|
|
307
|
+
|
|
308
|
+
Pre-release of 0.5.0: its changes are listed under "0.5.0 (unreleased)" above.
|
|
309
|
+
|
|
7
310
|
## 0.4.0 (2026-10-29)
|
|
8
311
|
|
|
9
312
|
### timeline
|
package/README.md
CHANGED
|
@@ -140,17 +140,17 @@ Render library components only in the browser, for example in a `@defer` block o
|
|
|
140
140
|
route. Support will be decided before 1.0.
|
|
141
141
|
|
|
142
142
|
The one server-side check is a render smoke, not a support test: the library repository's
|
|
143
|
-
`tools/ssr-smoke.ts` renders
|
|
144
|
-
`bf-number-input`, a `/charts` line chart, a `/mobile` page with
|
|
145
|
-
`bf-header` and `bf-content`, `bf-tabs` with `bf-stack-outlet` under
|
|
146
|
-
dialog), and all of them render without an error.
|
|
147
|
-
number their ids differently on every render, from a module-level
|
|
148
|
-
hydrate: `bf-app-frame`, `bf-calendar`, `bf-checkbox`, `bf-input`, `bf-
|
|
149
|
-
`bf-
|
|
150
|
-
`bf-toggle`, `bf-line-chart`, `bf-number-input` and the `/overlay`
|
|
151
|
-
not supported or tested, and more stands in its way: `/mobile`
|
|
152
|
-
keyboard probe to `<body>`, and an `/overlay` dialog renders outside
|
|
153
|
-
inert.
|
|
143
|
+
`tools/ssr-smoke.ts` renders 49 fixtures with `renderApplication()` (every root widget, `bf-icon`,
|
|
144
|
+
`bf-number-input`, `bf-select` from `/overlay`, a `/charts` line chart, a `/mobile` page with
|
|
145
|
+
`provideBfShell()`, `bf-page`, `bf-header` and `bf-content`, `bf-tabs` with `bf-stack-outlet` under
|
|
146
|
+
the router, and an `/overlay` dialog), and all of them render without an error. 31 of them render
|
|
147
|
+
identically twice. The other 18 number their ids differently on every render, from a module-level
|
|
148
|
+
counter, so they could not hydrate: `bf-app-frame`, `bf-calendar`, `bf-checkbox`, `bf-file-input`, `bf-image-input`, `bf-input`,
|
|
149
|
+
`bf-radio-group`, `bf-segmented`, `bf-select`, `bf-sidebar`, `bf-tab-group`, `bf-table`,
|
|
150
|
+
`bf-textarea`, `bf-theme-switch`, `bf-toggle`, `bf-line-chart`, `bf-number-input` and the `/overlay`
|
|
151
|
+
dialog. Hydration itself is still not supported or tested, and more stands in its way: `/mobile`
|
|
152
|
+
writes to `<html>` and adds a keyboard probe to `<body>`, and an `/overlay` dialog renders outside
|
|
153
|
+
the app root and marks it inert.
|
|
154
154
|
|
|
155
155
|
## Testing
|
|
156
156
|
|
|
@@ -163,7 +163,7 @@ overlays and themes leave on the document after each test (when TestBed destroys
|
|
|
163
163
|
environment, so keep its default `teardown: { destroyAfterEach: true }`), and the CDK harnesses
|
|
164
164
|
(`BfSelectHarness`, `BfRadioGroupHarness`, `BfSegmentedHarness`, `BfDialogHarness`,
|
|
165
165
|
`BfMenuButtonHarness`, `BfMenuHarness`, `BfMenuItemHarness`, `BfDesktopToastHarness`,
|
|
166
|
-
`BfTableHarness`, `BfPaginationHarness`, `BfTabGroupHarness`, `BfCalendarHarness`, `BfChartHarness`) drive the
|
|
166
|
+
`BfTableHarness`, `BfPaginationHarness`, `BfTabGroupHarness`, `BfCalendarHarness`, `BfFileInputHarness`, `BfImageInputHarness`, `BfMediaCardHarness`, `BfImageViewerHarness`, `BfChatComposerHarness`, `BfChartHarness`) drive the
|
|
167
167
|
widgets whose DOM a spec should not query by hand; overlays come from
|
|
168
168
|
`TestbedHarnessEnvironment.documentRootLoader(fixture)`. An app on the mobile shell uses
|
|
169
169
|
`provideBfShellTesting()` from `@bitforgehq/ui/mobile` instead (router, shell, an in-memory theme
|
package/ai/MANIFEST.index.json
CHANGED
|
@@ -136,6 +136,33 @@
|
|
|
136
136
|
"summary": "Card around one chart with a title, period control and loading, empty and error states",
|
|
137
137
|
"since": "0.3.1"
|
|
138
138
|
},
|
|
139
|
+
{
|
|
140
|
+
"name": "chat-composer",
|
|
141
|
+
"selector": "bf-chat-composer",
|
|
142
|
+
"className": "BfChatComposerComponent",
|
|
143
|
+
"category": "Forms",
|
|
144
|
+
"entryPoint": "@bitforgehq/ui/overlay",
|
|
145
|
+
"summary": "Chat input that grows with the text and sends on Enter, or stops a reply",
|
|
146
|
+
"since": "0.5.0"
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
"name": "chat-message",
|
|
150
|
+
"selector": "bf-chat-message",
|
|
151
|
+
"className": "BfChatMessageComponent",
|
|
152
|
+
"category": "Data display",
|
|
153
|
+
"entryPoint": "@bitforgehq/ui",
|
|
154
|
+
"summary": "One chat message, a user bubble or a full-width assistant reply that can stream",
|
|
155
|
+
"since": "0.5.0"
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
"name": "chat-thread",
|
|
159
|
+
"selector": "bf-chat-thread",
|
|
160
|
+
"className": "BfChatThreadComponent",
|
|
161
|
+
"category": "Data display",
|
|
162
|
+
"entryPoint": "@bitforgehq/ui",
|
|
163
|
+
"summary": "Scrolling chat message log that follows new text and offers a jump to latest",
|
|
164
|
+
"since": "0.5.0"
|
|
165
|
+
},
|
|
139
166
|
{
|
|
140
167
|
"name": "checkbox",
|
|
141
168
|
"selector": "bf-checkbox",
|
|
@@ -262,6 +289,15 @@
|
|
|
262
289
|
"summary": "Label, help text and error text wrapper that wires ids for assistive tech",
|
|
263
290
|
"since": "0.1.0"
|
|
264
291
|
},
|
|
292
|
+
{
|
|
293
|
+
"name": "file-input",
|
|
294
|
+
"selector": "bf-file-input",
|
|
295
|
+
"className": "BfFileInputComponent",
|
|
296
|
+
"category": "Forms",
|
|
297
|
+
"entryPoint": "@bitforgehq/ui",
|
|
298
|
+
"summary": "File picker field that lists picked or dropped files and checks type, size and count",
|
|
299
|
+
"since": "0.5.0"
|
|
300
|
+
},
|
|
265
301
|
{
|
|
266
302
|
"name": "header",
|
|
267
303
|
"selector": "bf-header",
|
|
@@ -289,6 +325,24 @@
|
|
|
289
325
|
"summary": "Round icon-only button for toolbars and rows, with a required accessible label",
|
|
290
326
|
"since": "0.1.0"
|
|
291
327
|
},
|
|
328
|
+
{
|
|
329
|
+
"name": "image-input",
|
|
330
|
+
"selector": "bf-image-input",
|
|
331
|
+
"className": "BfImageInputComponent",
|
|
332
|
+
"category": "Forms",
|
|
333
|
+
"entryPoint": "@bitforgehq/ui",
|
|
334
|
+
"summary": "Single-image picker with a round or square preview tile and replace or remove",
|
|
335
|
+
"since": "0.5.0"
|
|
336
|
+
},
|
|
337
|
+
{
|
|
338
|
+
"name": "image-viewer",
|
|
339
|
+
"selector": "bf-image-viewer",
|
|
340
|
+
"className": "BfImageViewerComponent",
|
|
341
|
+
"category": "Overlays",
|
|
342
|
+
"entryPoint": "@bitforgehq/ui/overlay",
|
|
343
|
+
"summary": "Full-screen image viewer that zooms from a thumbnail and steps through a set",
|
|
344
|
+
"since": "0.5.0"
|
|
345
|
+
},
|
|
292
346
|
{
|
|
293
347
|
"name": "input",
|
|
294
348
|
"selector": "bf-input",
|
|
@@ -325,6 +379,15 @@
|
|
|
325
379
|
"summary": "List row with leading slot, title, subtitle, trailing slot and an optional chevron",
|
|
326
380
|
"since": "0.1.0"
|
|
327
381
|
},
|
|
382
|
+
{
|
|
383
|
+
"name": "media-card",
|
|
384
|
+
"selector": "bf-media-card",
|
|
385
|
+
"className": "BfMediaCardComponent",
|
|
386
|
+
"category": "Data display",
|
|
387
|
+
"entryPoint": "@bitforgehq/ui",
|
|
388
|
+
"summary": "Card with an image that loads, fades in or fails, plus a body and actions",
|
|
389
|
+
"since": "0.5.0"
|
|
390
|
+
},
|
|
328
391
|
{
|
|
329
392
|
"name": "menu",
|
|
330
393
|
"selector": "bf-menu",
|
|
@@ -465,8 +528,8 @@
|
|
|
465
528
|
"selector": "bf-select",
|
|
466
529
|
"className": "BfSelectComponent",
|
|
467
530
|
"category": "Forms",
|
|
468
|
-
"entryPoint": "@bitforgehq/ui",
|
|
469
|
-
"summary": "
|
|
531
|
+
"entryPoint": "@bitforgehq/ui/overlay",
|
|
532
|
+
"summary": "Single-choice dropdown with a styled list, native under touch, with Signal Forms binding",
|
|
470
533
|
"since": "0.1.0"
|
|
471
534
|
},
|
|
472
535
|
{
|