@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
package/ai/UI_GUIDE.md
CHANGED
|
@@ -72,7 +72,7 @@ In `src/index.html`, give `<body>` the theme background and text colour, so the
|
|
|
72
72
|
|
|
73
73
|
### 7. Switch to dark mode
|
|
74
74
|
|
|
75
|
-
Add the `dark` class to `<html>` (and remove it for light mode). Every token is redefined under `.dark`, and the theme sets `color-scheme`, so scrollbars and native controls follow too. Components need no `dark:` classes. To let users choose System, Light or Dark, add `provideBfColorScheme()` to the app providers: it sets the class, follows the OS and remembers the choice.
|
|
75
|
+
Add the `dark` class to `<html>` (and remove it for light mode). Every token is redefined under `.dark`, and the theme sets `color-scheme`, so scrollbars and native controls follow too. Components need no `dark:` classes. To let users choose System, Light or Dark, add `provideBfColorScheme()` to the app providers: it sets the class, follows the OS and remembers the choice. For a near-black dark mode, add `@import '@bitforgehq/ui/dark-ink.css';` right after the theme import: the Ink preset, which changes dark mode only.
|
|
76
76
|
|
|
77
77
|
```html
|
|
78
78
|
<html lang="en" class="dark">
|
|
@@ -123,6 +123,10 @@ pressed, soft and strong steps, `on-primary`, `primary-text`, `selected`, `ring`
|
|
|
123
123
|
and a `.dark` block with each contrast pair's ratio, and exits 1 naming the pairs below their
|
|
124
124
|
minimum and a shade that passes; paste its output, never hand-pick the steps. Example (the
|
|
125
125
|
Meadow Sage green): `bunx bf-ui theme '#238652' --dark '#5cc48a'`.
|
|
126
|
+
For a near-black dark mode, import the opt-in Ink preset right after the theme,
|
|
127
|
+
`@import '@bitforgehq/ui/dark-ink.css';`: it redefines every dark token but the brand set under
|
|
128
|
+
`.dark` (light mode and the default dark stay as they are), so a brand `.dark` block goes after
|
|
129
|
+
it. Never copy its values into `.dark` by hand, and never put `dark:` classes on widgets for it.
|
|
126
130
|
`--radius-control` (`9999px`, pills) reshapes buttons, chips, badges, segmented, the tab bar, the
|
|
127
131
|
action bar and toggles at once:
|
|
128
132
|
|
|
@@ -143,17 +147,17 @@ Render library components only in the browser, for example in a `@defer` block o
|
|
|
143
147
|
route. Support will be decided before 1.0.
|
|
144
148
|
|
|
145
149
|
The one server-side check is a render smoke, not a support test: the library repository's
|
|
146
|
-
`tools/ssr-smoke.ts` renders
|
|
147
|
-
`bf-number-input`, a `/charts` line chart, a `/mobile` page with
|
|
148
|
-
`bf-header` and `bf-content`, `bf-tabs` with `bf-stack-outlet` under
|
|
149
|
-
dialog), and all of them render without an error.
|
|
150
|
-
number their ids differently on every render, from a module-level
|
|
151
|
-
hydrate: `bf-app-frame`, `bf-calendar`, `bf-checkbox`, `bf-input`, `bf-
|
|
152
|
-
`bf-
|
|
153
|
-
`bf-toggle`, `bf-line-chart`, `bf-number-input` and the `/overlay`
|
|
154
|
-
not supported or tested, and more stands in its way: `/mobile`
|
|
155
|
-
keyboard probe to `<body>`, and an `/overlay` dialog renders outside
|
|
156
|
-
inert.
|
|
150
|
+
`tools/ssr-smoke.ts` renders 49 fixtures with `renderApplication()` (every root widget, `bf-icon`,
|
|
151
|
+
`bf-number-input`, `bf-select` from `/overlay`, a `/charts` line chart, a `/mobile` page with
|
|
152
|
+
`provideBfShell()`, `bf-page`, `bf-header` and `bf-content`, `bf-tabs` with `bf-stack-outlet` under
|
|
153
|
+
the router, and an `/overlay` dialog), and all of them render without an error. 31 of them render
|
|
154
|
+
identically twice. The other 18 number their ids differently on every render, from a module-level
|
|
155
|
+
counter, so they could not hydrate: `bf-app-frame`, `bf-calendar`, `bf-checkbox`, `bf-file-input`, `bf-image-input`, `bf-input`,
|
|
156
|
+
`bf-radio-group`, `bf-segmented`, `bf-select`, `bf-sidebar`, `bf-tab-group`, `bf-table`,
|
|
157
|
+
`bf-textarea`, `bf-theme-switch`, `bf-toggle`, `bf-line-chart`, `bf-number-input` and the `/overlay`
|
|
158
|
+
dialog. Hydration itself is still not supported or tested, and more stands in its way: `/mobile`
|
|
159
|
+
writes to `<html>` and adds a keyboard probe to `<body>`, and an `/overlay` dialog renders outside
|
|
160
|
+
the app root and marks it inert.
|
|
157
161
|
|
|
158
162
|
## Keeping this guide in sync
|
|
159
163
|
|
|
@@ -172,6 +176,10 @@ or out of date — wire it into CI so agent guidance never drifts from the insta
|
|
|
172
176
|
- Number field: `import { BfNumberInputComponent } from '@bitforgehq/ui/number-input';`. It is
|
|
173
177
|
the one widget in its own entry point, because it loads Angular Forms (`@angular/forms`, a
|
|
174
178
|
required peer); `@bitforgehq/ui` itself loads no forms code, so an app without forms ships none.
|
|
179
|
+
- Select: `import { BfSelectComponent } from '@bitforgehq/ui/overlay';` (needs `@angular/cdk`;
|
|
180
|
+
`BfSelectOption` is also exported from `@bitforgehq/ui`). Under a mouse it opens a styled list,
|
|
181
|
+
under touch it renders the native `<select>`. Add `provideBfSelect()` to the app's providers so a
|
|
182
|
+
stacked `bf-table`'s "Sort by" control is the styled select too (without it, the native one).
|
|
175
183
|
- Charts: `import { BfLineChartComponent } from '@bitforgehq/ui/charts';` (also `BfBarChartComponent`,
|
|
176
184
|
`BfDonutChartComponent`, `BfSparklineComponent`, `BfChartCardComponent`). Our own SVG charts,
|
|
177
185
|
themed with the `--color-chart-1` … `--color-chart-8` tokens; pass `series` (`{ name, values }`) and
|
|
@@ -182,7 +190,7 @@ or out of date — wire it into CI so agent guidance never drifts from the insta
|
|
|
182
190
|
`border-border`, `rounded-field`, `shadow-raised`, `text-body`). Never raw palette classes and
|
|
183
191
|
never `dark:` variants; put `dark` on `<html>` and the theme re-colors everything.
|
|
184
192
|
- Forms: `bf-input`, `bf-number-input`, `bf-textarea`, `bf-select`, `bf-checkbox`, `bf-toggle`,
|
|
185
|
-
`bf-radio-group` and `bf-
|
|
193
|
+
`bf-radio-group`, `bf-segmented`, `bf-file-input` and `bf-image-input` expose `value` / `checked` as `model()` and implement
|
|
186
194
|
Signal Forms' `FormValueControl` / `FormCheckboxControl`. Bind either `[formField]="form.field"` or
|
|
187
195
|
`[(value)]` / `[(checked)]`, never both. Import `FormField`, `FormRoot`, `form` and the
|
|
188
196
|
validators from `@angular/forms/signals`. The first error shows once the field is touched: its
|
|
@@ -200,6 +208,14 @@ or out of date — wire it into CI so agent guidance never drifts from the insta
|
|
|
200
208
|
(`number | null`, `null` while empty; `min()`/`max()` reach the native input; unreadable text is
|
|
201
209
|
a `parse` error on the bound field, blocking `submit()`), never to a `string` model on
|
|
202
210
|
`bf-input type="number"`.
|
|
211
|
+
- Files: `bf-file-input` binds a `File[]` (`multiple` for several; `variant="dropzone"` for a drop
|
|
212
|
+
area) and `bf-image-input` a `File | null` (`[(src)]` shows the saved picture; Remove sets both
|
|
213
|
+
to null). Both only pick: upload from the form's `submission`. `accept`, `maxSize` and
|
|
214
|
+
`maxFiles` refuse a file without making the field invalid; read refusals from `(rejected)`
|
|
215
|
+
(`BfRejectedFile[]`). `required()` does not count an empty `File[]` as empty: add
|
|
216
|
+
`validate(p.files, ({ value }) => value().length === 0 ? requiredError() : undefined)`. For an
|
|
217
|
+
image with a `src`, give the marker with `metadata(p.avatar, REQUIRED, () => true)` and a
|
|
218
|
+
`validate()` that counts `src` too.
|
|
203
219
|
- Reactive Forms work through Angular 22's built-in interop: `[formControl]` and
|
|
204
220
|
`formControlName` bind every form widget above with no `ControlValueAccessor` (never add one).
|
|
205
221
|
Value, touched, dirty, validity, pending, disabled, required and errors flow into the widget;
|
|
@@ -238,7 +254,7 @@ you can guess a binding without looking it up:
|
|
|
238
254
|
spinner, tab bar, progress or icon); every component that renders a native input, select or
|
|
239
255
|
textarea (checkbox and toggle included) also takes `ariaLabel` and `ariaLabelledby` for when no
|
|
240
256
|
visible `label` is shown; a built-in sub-control's name is `<part>Label` (`dismissLabel`,
|
|
241
|
-
`closeLabel`, `actionLabel`); `alt` only on `bf-avatar`.
|
|
257
|
+
`closeLabel`, `actionLabel`); `alt` only on `bf-avatar` and `bf-media-card`.
|
|
242
258
|
- Types: every union or object type of an input, model, output, `Bf…Options`/`Bf…Config` property
|
|
243
259
|
or an interface they name is a named type exported from the same entry
|
|
244
260
|
(`BfToggleLabelPosition`, `BfRefresherRefreshEvent`, `BfDialogRole`, …), so type your own state
|
|
@@ -260,7 +276,8 @@ toolbar or a German settings page); a key you leave out keeps the parent's value
|
|
|
260
276
|
|
|
261
277
|
- `provideBfDefaults({ size, fieldSize, controlSize })` sets the height step (`BfSize`: `sm` 32,
|
|
262
278
|
`md` 40, `lg` 48 px) a widget uses when its own `size` input is unset. `fieldSize` covers the
|
|
263
|
-
text fields (`bf-input`, `bf-number-input`, `bf-select`, `bf-textarea
|
|
279
|
+
text fields (`bf-input`, `bf-number-input`, `bf-select`, `bf-textarea`, and the file pickers
|
|
280
|
+
`bf-file-input`, `bf-image-input`), `controlSize` the
|
|
264
281
|
actions and choice controls (`bf-button`, `bf-icon-button`, `bf-chip`, `bf-segmented`,
|
|
265
282
|
`bf-checkbox`, `bf-toggle`, `bf-radio-group`); `size` sets both, and an explicit
|
|
266
283
|
`fieldSize`/`controlSize` wins over it. The library default is compact `md` for both (desktop
|
|
@@ -374,10 +391,31 @@ protected readonly colorScheme = inject(BfColorSchemeService);
|
|
|
374
391
|
sorts the rows in a `computed()`, and a selection shows a `bf-action-bar`. `stack="narrow"`
|
|
375
392
|
(below 36rem of its own width) or `stack="coarse"` turns each row into a label/value tile with
|
|
376
393
|
a sort and select bar; in the Capacitor shell use `bf-list-item` rows instead.
|
|
394
|
+
A settings matrix puts a `bf-toggle` in each cell (`align: 'center'` columns, the row label
|
|
395
|
+
first, every toggle named `[ariaLabel]="column + ', ' + row"`).
|
|
377
396
|
- **Details:** `bf-description-list` of `div[bfDescriptionItem]` label/value pairs, and `bf-stat`
|
|
378
397
|
figure tiles in a grid; both work in both shells.
|
|
398
|
+
- **History:** `<bf-timeline label="…">` of `<bf-timeline-event actor="…" [time]="iso" tone="…">`
|
|
399
|
+
(text with inline links in the content, a small action in `bfSlot="end"`) for a record's
|
|
400
|
+
history or an audit trail, in the order the app gives; times follow `LOCALE_ID` (`timeFormat`,
|
|
401
|
+
`timeText` for a relative time). Not for navigation or steps.
|
|
402
|
+
- **Chat:** a `bf-chat-thread` (give it `class="flex-1"` or a height) of `bf-chat-message`
|
|
403
|
+
elements (`role` `user` or `assistant`, `author`, `streaming`, `status`) above a
|
|
404
|
+
`bf-chat-composer` from `/overlay` (`[(value)]="draft"`, `[running]="replying()"`,
|
|
405
|
+
`(sent)="ask($event)"`, `(stopped)="cancel()"`). A message's content is the app's rendered,
|
|
406
|
+
sanitised markdown, styled by the body's `bfProse`; cards and chips go in `bfSlot="content"`,
|
|
407
|
+
Retry in `bfSlot="action"`; the composer's icons go in `bfSlot="send-icon"` and
|
|
408
|
+
`bfSlot="stop-icon"`, extra controls in `start` and `end`. The thread follows new text, shows
|
|
409
|
+
"Jump to latest" when scrolled up and is `aria-busy` while a message streams; never scroll or
|
|
410
|
+
animate it yourself. Set `streaming` false when the reply ends, stops or fails.
|
|
379
411
|
- **Cards in a grid:** the grid stretches each `bf-card` and its `bfSlot="footer"` sits on the
|
|
380
412
|
bottom edge, so footers line up; put a footer's parts in one element.
|
|
413
|
+
- **Pictures that are slow or can fail:** `bf-media-card` owns the picture (`[src]`, `alt`,
|
|
414
|
+
`[loading]`, `loadingLabel="Drawing…"`, `[failed]`): a skeleton while it loads (or while `src`
|
|
415
|
+
is empty and `loading`), a fade when it arrives, a message and `bfSlot="failed-action"` (Retry)
|
|
416
|
+
when it fails; retry with a changed `src`. Never project your own `<img>` for the main picture.
|
|
417
|
+
Clickable like `bf-card` (a `bfAction` in the title); `selectable` with `[(selected)]` and
|
|
418
|
+
`label` for a pick-several grid.
|
|
381
419
|
- **Rows on a card:** `<bf-list [inset]="false" divided class="-mx-card">`, flush to the card's
|
|
382
420
|
edges with lines between the rows.
|
|
383
421
|
- **Theme switch:** `<bf-theme-switch [(value)]="colorScheme.scheme" />` (or
|
|
@@ -395,8 +433,8 @@ protected readonly colorScheme = inject(BfColorSchemeService);
|
|
|
395
433
|
for the platform picker on touch). Values are ISO strings (`'2026-10-28'`, `'14:30'`,
|
|
396
434
|
`'2026-10-28T14:30'`); month and day names come from `Intl` in the document's language.
|
|
397
435
|
- **Picking from many options:** `bf-combobox` (`/overlay`) for one value with search, groups and
|
|
398
|
-
server search (`queried`, `loading`); `bf-multi-combobox` for tags. `bf-select`
|
|
399
|
-
for a short fixed list.
|
|
436
|
+
server search (`queried`, `loading`); `bf-multi-combobox` for tags. `bf-select` (`/overlay`
|
|
437
|
+
too) stays the choice for a short fixed list.
|
|
400
438
|
- **Dashboards:** `bf-stat` with `delta`, `trend` and `trendIntent` for a figure and its change;
|
|
401
439
|
`@bitforgehq/ui/charts` for `bf-line-chart`, `bf-bar-chart`, `bf-donut-chart` and
|
|
402
440
|
`bf-sparkline` inside a `bf-chart-card` (title, period slot, loading, empty and error states),
|
|
@@ -408,6 +446,13 @@ protected readonly colorScheme = inject(BfColorSchemeService);
|
|
|
408
446
|
- **Empty pages:** a 404, "no access" or "nothing here yet" page is a `bf-empty-state` with
|
|
409
447
|
`headingLevel` 1, centred in the content area, with the main action and an optional link.
|
|
410
448
|
- **Toolbar on a phone:** `bf-toolbar` keeps its status and actions together on the last row.
|
|
449
|
+
- **Severity:** soft `bf-badge` tones for most states; `emphasis="strong"` (a solid fill, also on
|
|
450
|
+
a count pill) only for the one that must stand out, such as `variant="danger"` "Critical".
|
|
451
|
+
- **Inbox and notifications:** `bf-list-item [unread]="!item.read"` (dot, bolder title, "Unread"
|
|
452
|
+
read before the title, also in its `bfAction`'s name); clear it when the row is opened.
|
|
453
|
+
- **Instructions in order:** `<bf-steps>` of `<bf-step title="Install the app">…details…</bf-step>`
|
|
454
|
+
(`start` to continue a list) for setup steps in both shells; not a wizard (a multi-page flow
|
|
455
|
+
with state is a set of pages).
|
|
411
456
|
|
|
412
457
|
## Overlays (`@bitforgehq/ui/overlay`)
|
|
413
458
|
|
|
@@ -445,6 +490,12 @@ injector? })` stacks up to three cards bottom-right (configurable with
|
|
|
445
490
|
- **Side panel:** `BfSidePanelService.open(component | template, { width, modal, data })` slides a
|
|
446
491
|
full-height `bf-side-panel` in from the inline end: a record's details or edit form beside the
|
|
447
492
|
list. `modal: false` keeps the page usable. In the Capacitor shell use `bf-sheet`/`bf-modal`.
|
|
493
|
+
- **Image viewer:** `BfImageViewerService.open(options)` with `images` (each
|
|
494
|
+
`{ src, label, caption? }`), `index`, `origin`, `actions` and `injector` shows one image full
|
|
495
|
+
screen over a dark scrim and steps through the set (buttons, arrow keys, Home/End, wrapping);
|
|
496
|
+
pass the clicked thumbnail as `origin` (or `(i) => thumbnails[i]`) so it zooms out of it and
|
|
497
|
+
back. Actions go in an `ng-template` (context `{ $implicit: image, index, ref }`). It returns a
|
|
498
|
+
`BfImageViewerRef` (`index()`, `next()`, `previous()`, `goTo()`, `close()`, `afterClosed`).
|
|
448
499
|
- **`closeOnNavigation`:** every overlay (`BfDialogConfig`, `confirm()`, and the `/mobile` sheet,
|
|
449
500
|
modal, action sheet and alert options) closes when the URL path changes by default (`true`);
|
|
450
501
|
pass `closeOnNavigation: false` to keep one open across a navigation (e.g. a draft that must
|
|
@@ -976,7 +1027,7 @@ describe('bf-button in a consumer app (Vitest)', () => {
|
|
|
976
1027
|
|
|
977
1028
|
### 6. Drive a form widget through its harness
|
|
978
1029
|
|
|
979
|
-
The entry ships CDK harnesses for the widgets whose markup is not obvious to drive: `BfSelectHarness`, `BfRadioGroupHarness`, `BfSegmentedHarness`, `BfDialogHarness`, `BfMenuHarness`, `BfMenuItemHarness`, `BfMenuButtonHarness`, `BfDesktopToastHarness`, `BfTableHarness`, `BfPaginationHarness` `BfTabGroupHarness`, `BfCalendarHarness` and `BfChartHarness`. `with({ label })` finds a widget by its label. Load an in-page widget with `TestbedHarnessEnvironment.loader(fixture)`.
|
|
1030
|
+
The entry ships CDK harnesses for the widgets whose markup is not obvious to drive: `BfSelectHarness`, `BfRadioGroupHarness`, `BfSegmentedHarness`, `BfDialogHarness`, `BfMenuHarness`, `BfMenuItemHarness`, `BfMenuButtonHarness`, `BfDesktopToastHarness`, `BfTableHarness`, `BfToggleHarness`, `BfPaginationHarness` `BfTabGroupHarness`, `BfCalendarHarness`, `BfFileInputHarness`, `BfImageInputHarness`, `BfMediaCardHarness`, `BfChatComposerHarness` and `BfChartHarness`. `with({ label })` finds a widget by its label. Load an in-page widget with `TestbedHarnessEnvironment.loader(fixture)`. jsdom has no `URL.createObjectURL`, so after `addFile()` `BfImageInputHarness.hasPreview()` stays false: assert on the bound value, or stub `URL.createObjectURL` to read the preview.
|
|
980
1031
|
|
|
981
1032
|
`select-harness.spec.ts`:
|
|
982
1033
|
|
|
@@ -989,7 +1040,7 @@ import { Component, signal } from '@angular/core';
|
|
|
989
1040
|
import { TestBed } from '@angular/core/testing';
|
|
990
1041
|
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
|
|
991
1042
|
import { FormField, form, required } from '@angular/forms/signals';
|
|
992
|
-
import { BfSelectComponent, type BfSelectOption } from '@bitforgehq/ui';
|
|
1043
|
+
import { BfSelectComponent, type BfSelectOption } from '@bitforgehq/ui/overlay';
|
|
993
1044
|
import { BfSelectHarness, provideBfTesting } from '@bitforgehq/ui/testing';
|
|
994
1045
|
|
|
995
1046
|
@Component({
|
|
@@ -1046,7 +1097,7 @@ describe('BfSelectHarness in a consumer app (Vitest)', () => {
|
|
|
1046
1097
|
|
|
1047
1098
|
### 7. Drive a dialog from the document root
|
|
1048
1099
|
|
|
1049
|
-
Dialogs, menus and toasts render in the CDK overlay container at the end of `<body>`, outside the fixture. Load their harnesses with `TestbedHarnessEnvironment.documentRootLoader(fixture)`.
|
|
1100
|
+
Dialogs, menus and toasts render in the CDK overlay container at the end of `<body>`, outside the fixture. Load their harnesses with `TestbedHarnessEnvironment.documentRootLoader(fixture)`; `BfImageViewerHarness` drives the image viewer the same way. jsdom never loads images: `BfMediaCardHarness` and `BfImageViewerHarness` fire the `load` and `error` events with `loadImage()` and `failImage()`.
|
|
1050
1101
|
|
|
1051
1102
|
`dialog-harness.spec.ts`:
|
|
1052
1103
|
|
|
@@ -1203,6 +1254,7 @@ notes.
|
|
|
1203
1254
|
| Component | Entry point | Purpose |
|
|
1204
1255
|
| ------------------- | ----------------------------- | ------------------------------------------------------------------------------------------- |
|
|
1205
1256
|
| `bf-calendar` | `@bitforgehq/ui` | Inline month calendar for picking one date, with month and year jumps |
|
|
1257
|
+
| `bf-chat-composer` | `@bitforgehq/ui/overlay` | Chat input that grows with the text and sends on Enter, or stops a reply |
|
|
1206
1258
|
| `bf-checkbox` | `@bitforgehq/ui` | Rounded checkbox with an optional label that binds to Signal Forms |
|
|
1207
1259
|
| `bf-chip` | `@bitforgehq/ui` | Toggleable filter chip with a check mark when selected, in tonal or ink style |
|
|
1208
1260
|
| `bf-chip-group` | `@bitforgehq/ui` | Named row of chips that picks one like radios, or groups independent toggles |
|
|
@@ -1210,12 +1262,14 @@ notes.
|
|
|
1210
1262
|
| `bf-date-input` | `@bitforgehq/ui/overlay` | Typed date field with a calendar popover, valued as an ISO date string |
|
|
1211
1263
|
| `bf-datetime-input` | `@bitforgehq/ui/overlay` | Typed date and time field with a calendar and time list in one popover |
|
|
1212
1264
|
| `bf-field` | `@bitforgehq/ui` | Label, help text and error text wrapper that wires ids for assistive tech |
|
|
1265
|
+
| `bf-file-input` | `@bitforgehq/ui` | File picker field that lists picked or dropped files and checks type, size and count |
|
|
1266
|
+
| `bf-image-input` | `@bitforgehq/ui` | Single-image picker with a round or square preview tile and replace or remove |
|
|
1213
1267
|
| `bf-input` | `@bitforgehq/ui` | Single-line text field with label, help and error text, icon slots and Signal Forms binding |
|
|
1214
1268
|
| `bf-multi-combobox` | `@bitforgehq/ui/overlay` | Text field that filters options as you type and picks several as tags |
|
|
1215
1269
|
| `bf-number-input` | `@bitforgehq/ui/number-input` | Number field that binds number or null, with native min, max and step |
|
|
1216
1270
|
| `bf-radio-group` | `@bitforgehq/ui` | Group of radio buttons for choosing one option, with optional per-option descriptions |
|
|
1217
1271
|
| `bf-segmented` | `@bitforgehq/ui` | Segmented control for switching between two to four views, keyboard-navigable like radios |
|
|
1218
|
-
| `bf-select` | `@bitforgehq/ui`
|
|
1272
|
+
| `bf-select` | `@bitforgehq/ui/overlay` | Single-choice dropdown with a styled list, native under touch, with Signal Forms binding |
|
|
1219
1273
|
| `bf-textarea` | `@bitforgehq/ui` | Multi-line text field with label, help and error text, and Signal Forms binding |
|
|
1220
1274
|
| `bf-theme-switch` | `@bitforgehq/ui` | Segmented System, Light and Dark switch bound two-way to the app colour scheme |
|
|
1221
1275
|
| `bf-time-input` | `@bitforgehq/ui/overlay` | Typed time-of-day field with a popover list of times, valued as an ISO string |
|
|
@@ -1230,16 +1284,23 @@ notes.
|
|
|
1230
1284
|
| `bf-bar-chart` | `@bitforgehq/ui/charts` | Bar chart of series over categories, grouped or stacked, vertical or horizontal |
|
|
1231
1285
|
| `bf-card` | `@bitforgehq/ui` | Content surface with an optional header slot, in four visual styles from raised to flat |
|
|
1232
1286
|
| `bf-chart-card` | `@bitforgehq/ui/charts` | Card around one chart with a title, period control and loading, empty and error states |
|
|
1287
|
+
| `bf-chat-message` | `@bitforgehq/ui` | One chat message, a user bubble or a full-width assistant reply that can stream |
|
|
1288
|
+
| `bf-chat-thread` | `@bitforgehq/ui` | Scrolling chat message log that follows new text and offers a jump to latest |
|
|
1233
1289
|
| `div[bfDescriptionItem]` | `@bitforgehq/ui` | One label and value pair of a description list, label as term, content as value |
|
|
1234
1290
|
| `bf-description-list` | `@bitforgehq/ui` | Native description list of label-value pairs, as divided rows or a tile grid |
|
|
1235
1291
|
| `bf-donut-chart` | `@bitforgehq/ui/charts` | Donut chart of the parts of a whole, with a legend, tooltip and centre slot |
|
|
1236
1292
|
| `bf-line-chart` | `@bitforgehq/ui/charts` | Line chart of series over categories, with tooltip, legend and data table |
|
|
1237
1293
|
| `bf-list` | `@bitforgehq/ui` | Container for list rows, inset by default with rounded corners on a warm background |
|
|
1238
1294
|
| `bf-list-item` | `@bitforgehq/ui` | List row with leading slot, title, subtitle, trailing slot and an optional chevron |
|
|
1295
|
+
| `bf-media-card` | `@bitforgehq/ui` | Card with an image that loads, fades in or fails, plus a body and actions |
|
|
1239
1296
|
| `bf-progress` | `@bitforgehq/ui` | Progress bar in two sizes and four tones for completion, quotas and unknown waits |
|
|
1240
1297
|
| `bf-sparkline` | `@bitforgehq/ui/charts` | Tiny trend line with no axes or legend, for stats and table cells |
|
|
1241
1298
|
| `bf-stat` | `@bitforgehq/ui` | One labelled figure with an optional icon tile and a supporting line |
|
|
1299
|
+
| `bf-step` | `@bitforgehq/ui` | One numbered step of an instruction list, with a title and its details |
|
|
1300
|
+
| `bf-steps` | `@bitforgehq/ui` | Numbered instruction steps a user follows in order, with joined markers |
|
|
1242
1301
|
| `bf-table` | `@bitforgehq/ui` | Desktop data table with sortable headers, row selection, custom cells and an empty slot |
|
|
1302
|
+
| `bf-timeline` | `@bitforgehq/ui` | Dated list of events with who did what and when, on a vertical line |
|
|
1303
|
+
| `bf-timeline-event` | `@bitforgehq/ui` | One timeline event with a coloured marker, its text, actor and time |
|
|
1243
1304
|
|
|
1244
1305
|
### Feedback
|
|
1245
1306
|
|
|
@@ -1284,6 +1345,7 @@ notes.
|
|
|
1284
1345
|
| `bf-alert-dialog` | `@bitforgehq/ui/mobile` | Centred confirmation dialog with a title, message and a row of role-styled buttons |
|
|
1285
1346
|
| `bf-desktop-toast` | `@bitforgehq/ui/overlay` | Desktop notification card in a bottom corner stack of up to three |
|
|
1286
1347
|
| `bf-dialog` | `@bitforgehq/ui/overlay` | Desktop modal dialog with a title, scrolling body, footer actions and focus trap |
|
|
1348
|
+
| `bf-image-viewer` | `@bitforgehq/ui/overlay` | Full-screen image viewer that zooms from a thumbnail and steps through a set |
|
|
1287
1349
|
| `bf-menu` | `@bitforgehq/ui/overlay` | Keyboard-operable menu panel with items, checkbox and radio items, groups and submenus |
|
|
1288
1350
|
| `bf-menu-button` | `@bitforgehq/ui/overlay` | Styled button that opens a menu, with outline, text, tonal and icon-only looks |
|
|
1289
1351
|
| `bf-modal` | `@bitforgehq/ui/mobile` | Modal overlay shown as an inset card over the scaled-down page, or full screen |
|
|
@@ -157,7 +157,7 @@ Use `bf-app-frame` once, at the root of a desktop web app whose top-level destin
|
|
|
157
157
|
|
|
158
158
|
## Slots and layout
|
|
159
159
|
|
|
160
|
-
Put the `bf-sidebar` in `bfSlot="sidebar"`, the app's name or logo in `bfSlot="brand"`, the narrow-screen `bf-menu-button` of `a[bfMenuItem]` router links in `bfSlot="menu"` and end actions (the theme menu, the account) in `bfSlot="actions"`; everything else is the page. From the breakpoint up the sidebar column sits at the inline start, `sticky top-0` and `h-dvh` tall, and the `bf-sidebar` inside fills it and scrolls its own destinations; the header bar is hidden. Below it the sidebar column is hidden and the header bar (`surface`, a bottom border, sticky at the top) shows the menu at the inline start, then the brand, and the actions at the end: the top app bar convention, and the menu comes first in the tab order too. The order is fixed, whatever order the slots have in your template. Keep the brand in the sidebar's own `header` slot too, for the wide layout.
|
|
160
|
+
Put the `bf-sidebar` in `bfSlot="sidebar"`, the app's name or logo in `bfSlot="brand"`, the narrow-screen `bf-menu-button` of `a[bfMenuItem]` router links in `bfSlot="menu"` and end actions (the theme menu, the account) in `bfSlot="actions"`; everything else is the page. From the breakpoint up the sidebar column sits at the inline start, `sticky top-0` and `h-dvh` tall, and the `bf-sidebar` inside fills it and scrolls its own destinations; the header bar is hidden. Below it the sidebar column is hidden and the header bar (`surface`, a bottom border, sticky at the top) shows the menu at the inline start, then the brand, and the actions at the end: the top app bar convention, and the menu comes first in the tab order too. The order is fixed, whatever order the slots have in your template. Keep the brand in the sidebar's own `header` slot too, for the wide layout. The sidebar column takes the sidebar's width, so when the sidebar collapses or expands the content column moves with its animation instead of jumping.
|
|
161
161
|
|
|
162
162
|
## Breakpoint and content width
|
|
163
163
|
|
package/ai/components/badge.md
CHANGED
|
@@ -66,6 +66,31 @@ import { BfBadgeComponent } from '@bitforgehq/ui';
|
|
|
66
66
|
export class BadgeStatusExampleComponent {}
|
|
67
67
|
```
|
|
68
68
|
|
|
69
|
+
## Emphasis
|
|
70
|
+
|
|
71
|
+
`emphasis="strong"` turns the soft tint into a solid fill with contrasting text, for the one state that must stand out from the rest: a "Critical" finding among "High" and "Medium" ones. It takes every variant (`primary` uses the brand fill) and the count pill too: a strong count takes its fill from `variant`, so `variant="danger" emphasis="strong" [count]="3"` is a red pill, while a soft count stays the primary pill. `emphasis` takes a `BfBadgeEmphasis` (`soft`, the default, or `strong`).
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
import { Component } from '@angular/core';
|
|
75
|
+
import { BfBadgeComponent } from '@bitforgehq/ui';
|
|
76
|
+
|
|
77
|
+
/** A strong badge marks the one critical finding among soft status badges. */
|
|
78
|
+
@Component({
|
|
79
|
+
selector: 'app-example-badge-strong',
|
|
80
|
+
imports: [BfBadgeComponent],
|
|
81
|
+
template: `
|
|
82
|
+
<div class="flex flex-wrap items-center gap-2">
|
|
83
|
+
<bf-badge variant="danger" emphasis="strong">Critical</bf-badge>
|
|
84
|
+
<bf-badge variant="danger">High</bf-badge>
|
|
85
|
+
<bf-badge variant="warning">Medium</bf-badge>
|
|
86
|
+
<bf-badge>Low</bf-badge>
|
|
87
|
+
<bf-badge variant="danger" emphasis="strong" [count]="3" countLabel="critical findings" />
|
|
88
|
+
</div>
|
|
89
|
+
`,
|
|
90
|
+
})
|
|
91
|
+
export class BadgeStrongExampleComponent {}
|
|
92
|
+
```
|
|
93
|
+
|
|
69
94
|
## Count pill
|
|
70
95
|
|
|
71
96
|
Setting `count` switches to the compact primary pill. Values above 99 render as "99+"; zero renders as "0".
|
|
@@ -128,17 +153,19 @@ Consumes tokens by variant, background then text:
|
|
|
128
153
|
- `danger`: `danger-soft` / `danger`
|
|
129
154
|
- `info`: `info-soft` / `info` (≥ 4.5:1 in both modes; it is not derived from the primary, so a custom brand colour leaves it unchanged)
|
|
130
155
|
- Count pill (`count` set): `primary` / `on-primary`, ignoring `variant`
|
|
156
|
+
- Strong (`emphasis="strong"`, label and count pill): `<variant>-strong` / `on-<variant>-strong` (`neutral-strong`, `success-strong`, `warning-strong`, `danger-strong`, `info-strong`, each with its `on-` text token; ≥ 4.5:1 in both modes and the fill ≥ 3:1 on `bg`, `surface` and `surface-sunken`); `primary` uses `primary` / `on-primary`. An app can retune `--color-danger-strong` and `--color-on-danger-strong` (and the others) in `@theme` and `.dark` like any status token
|
|
131
157
|
- Shape (label and count pill): `rounded-control` (`--radius-control`, `9999px` by default).
|
|
132
158
|
|
|
133
159
|
## Accessibility
|
|
134
160
|
|
|
135
|
-
A badge is a plain `<span>` with no role or ARIA attributes; its text is read as part of the surrounding content, so pair it with a label or context that says what it means rather than relying on color alone. A count pill with `countLabel` carries that context itself: a visually hidden (`sr-only`) span after the number, read as one phrase with it ("99+ unread messages").
|
|
161
|
+
A badge is a plain `<span>` with no role or ARIA attributes; its text is read as part of the surrounding content, so pair it with a label or context that says what it means rather than relying on color alone. A count pill with `countLabel` carries that context itself: a visually hidden (`sr-only`) span after the number, read as one phrase with it ("99+ unread messages"). A strong badge is louder, not more meaningful: its text ("Critical", "Kritisch") still says the state. In forced colours (Windows High Contrast) the fills give way to the system colours, so a strong badge draws a `CanvasText` outline and still reads as a filled pill next to a soft one.
|
|
136
162
|
|
|
137
163
|
## Inputs
|
|
138
164
|
|
|
139
165
|
| Name | Type | Default | Description |
|
|
140
166
|
| ------------ | ---------------------------------------------------------------------------------------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
141
|
-
| `variant` | `BfBadgeAppearance` (`'neutral' \| 'primary' \| 'success' \| 'warning' \| 'danger' \| 'info'`) | `'neutral'` | Tint for the label style: neutral, primary, success, warning, danger or info. Ignored
|
|
167
|
+
| `variant` | `BfBadgeAppearance` (`'neutral' \| 'primary' \| 'success' \| 'warning' \| 'danger' \| 'info'`) | `'neutral'` | Tint for the label style: neutral, primary, success, warning, danger or info. Ignored by a soft count pill; a strong one takes its fill from it. |
|
|
168
|
+
| `emphasis` | `BfBadgeEmphasis` (`'soft' \| 'strong'`) | `'soft'` | `soft` (a pale tint, the default) or `strong` (a solid fill with contrasting text, also for the count pill). |
|
|
142
169
|
| `count` | `number \| undefined` | `undefined` | Switches to the count pill; values above 99 render as "99+". |
|
|
143
170
|
| `countLabel` | `string` | `''` | What the count counts ("unread messages"), read by assistive tech after the number ("99+ unread messages"); visually hidden. Only used with `count`. |
|
|
144
171
|
|
|
@@ -152,15 +179,25 @@ A badge is a plain `<span>` with no role or ARIA attributes; its text is read as
|
|
|
152
179
|
|
|
153
180
|
- Keep labels to one or two words; badges are scanned, not read.
|
|
154
181
|
- Pair a count pill with a label or icon that says what is being counted, and set `countLabel` so the number is not read alone.
|
|
182
|
+
- Keep `emphasis="strong"` for the one state that must stand out on the screen, such as critical findings.
|
|
155
183
|
|
|
156
184
|
### Don't
|
|
157
185
|
|
|
158
186
|
- Don't use a badge as a button; it has no interactive states.
|
|
159
187
|
- Don't invent new colors on it; pick the variant that matches the meaning.
|
|
160
188
|
- Don't use `primary` or `success` for a category; use `info`, so the status tones keep their meaning.
|
|
189
|
+
- Don't make every badge strong: when all of them shout, none stands out.
|
|
161
190
|
|
|
162
191
|
## Migration notes
|
|
163
192
|
|
|
193
|
+
### 0.4.0
|
|
194
|
+
|
|
195
|
+
New `emphasis` input (`BfBadgeEmphasis`: `soft`, the default, or `strong`). `strong` is a solid
|
|
196
|
+
fill with contrasting text in every variant, for the one state that must stand out ("Critical");
|
|
197
|
+
`primary` uses the brand fill. A strong count pill takes its fill from `variant` (a soft count pill
|
|
198
|
+
stays primary, as before). In forced colours a strong badge keeps a `CanvasText` outline. Opt-in:
|
|
199
|
+
without `emphasis` nothing changes.
|
|
200
|
+
|
|
164
201
|
### 0.3.1
|
|
165
202
|
|
|
166
203
|
New `info` tone for informational labels such as a category, on the new `info` / `info-soft`
|