@bitforgehq/ui 0.3.0
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 +3665 -0
- package/LICENSE +21 -0
- package/README.md +175 -0
- package/ai/MANIFEST.index.json +383 -0
- package/ai/MANIFEST.json +7528 -0
- package/ai/REFERENCE.index.json +1461 -0
- package/ai/REFERENCE.json +7213 -0
- package/ai/UI_GUIDE.md +1159 -0
- package/ai/components/action-bar.md +209 -0
- package/ai/components/action-sheet.md +526 -0
- package/ai/components/alert-dialog.md +509 -0
- package/ai/components/alert.md +376 -0
- package/ai/components/avatar.md +146 -0
- package/ai/components/back-button.md +286 -0
- package/ai/components/badge.md +171 -0
- package/ai/components/button.md +431 -0
- package/ai/components/card.md +303 -0
- package/ai/components/checkbox.md +418 -0
- package/ai/components/chip-group.md +205 -0
- package/ai/components/chip.md +300 -0
- package/ai/components/content.md +474 -0
- package/ai/components/desktop-toast.md +420 -0
- package/ai/components/dialog.md +962 -0
- package/ai/components/empty-state.md +146 -0
- package/ai/components/field.md +319 -0
- package/ai/components/header.md +809 -0
- package/ai/components/icon-button.md +221 -0
- package/ai/components/icon.md +176 -0
- package/ai/components/input.md +455 -0
- package/ai/components/list-item.md +326 -0
- package/ai/components/list.md +178 -0
- package/ai/components/menu-button.md +377 -0
- package/ai/components/menu.md +661 -0
- package/ai/components/modal.md +613 -0
- package/ai/components/number-input.md +423 -0
- package/ai/components/page.md +299 -0
- package/ai/components/progress.md +215 -0
- package/ai/components/radio-group.md +432 -0
- package/ai/components/refresher.md +377 -0
- package/ai/components/segmented.md +517 -0
- package/ai/components/select.md +510 -0
- package/ai/components/sheet.md +845 -0
- package/ai/components/spinner.md +171 -0
- package/ai/components/stack-outlet.md +661 -0
- package/ai/components/tab-bar.md +427 -0
- package/ai/components/tab.md +295 -0
- package/ai/components/tabs.md +408 -0
- package/ai/components/textarea.md +371 -0
- package/ai/components/toast.md +522 -0
- package/ai/components/toggle.md +387 -0
- package/ai/reference/bf-action-directive.md +21 -0
- package/ai/reference/bf-action-sheet-action-role.md +27 -0
- package/ai/reference/bf-action-sheet-action.md +28 -0
- package/ai/reference/bf-action-sheet-options.md +28 -0
- package/ai/reference/bf-action-sheet-service.md +33 -0
- package/ai/reference/bf-alert-dialog-button-role.md +27 -0
- package/ai/reference/bf-alert-dialog-button.md +27 -0
- package/ai/reference/bf-alert-dialog-options.md +29 -0
- package/ai/reference/bf-alert-dialog-service.md +34 -0
- package/ai/reference/bf-alert-variant.md +28 -0
- package/ai/reference/bf-autocapitalize.md +30 -0
- package/ai/reference/bf-avatar-size.md +27 -0
- package/ai/reference/bf-avatar-variant.md +28 -0
- package/ai/reference/bf-back-at-root.md +28 -0
- package/ai/reference/bf-back-button-service.md +40 -0
- package/ai/reference/bf-badge-variant.md +29 -0
- package/ai/reference/bf-button-size.md +29 -0
- package/ai/reference/bf-button-type.md +28 -0
- package/ai/reference/bf-button-variant.md +29 -0
- package/ai/reference/bf-card-padding.md +26 -0
- package/ai/reference/bf-card-variant.md +28 -0
- package/ai/reference/bf-chip-group-mode.md +27 -0
- package/ai/reference/bf-chip-variant.md +26 -0
- package/ai/reference/bf-compare-with.md +24 -0
- package/ai/reference/bf-component-overlay-ref.md +33 -0
- package/ai/reference/bf-confirm-button-role.md +26 -0
- package/ai/reference/bf-confirm-button.md +27 -0
- package/ai/reference/bf-confirm-options.md +31 -0
- package/ai/reference/bf-confirm-size.md +26 -0
- package/ai/reference/bf-content-scroll-event.md +21 -0
- package/ai/reference/bf-defaults-options.md +32 -0
- package/ai/reference/bf-defaults.md +28 -0
- package/ai/reference/bf-desktop-toast-config.md +28 -0
- package/ai/reference/bf-desktop-toast-harness.md +33 -0
- package/ai/reference/bf-desktop-toast-options.md +28 -0
- package/ai/reference/bf-desktop-toast-position.md +28 -0
- package/ai/reference/bf-desktop-toast-service.md +42 -0
- package/ai/reference/bf-dialog-config.md +44 -0
- package/ai/reference/bf-dialog-harness.md +52 -0
- package/ai/reference/bf-dialog-ref.md +44 -0
- package/ai/reference/bf-dialog-role.md +26 -0
- package/ai/reference/bf-dialog-service.md +55 -0
- package/ai/reference/bf-dialog-size.md +31 -0
- package/ai/reference/bf-dialog-template-context.md +32 -0
- package/ai/reference/bf-empty-state-heading-level.md +26 -0
- package/ai/reference/bf-enter-key-hint.md +31 -0
- package/ai/reference/bf-field-control-class.md +32 -0
- package/ai/reference/bf-field-control-state.md +28 -0
- package/ai/reference/bf-field-size.md +29 -0
- package/ai/reference/bf-haptics-impact-style.md +27 -0
- package/ai/reference/bf-haptics-notification-type.md +27 -0
- package/ai/reference/bf-haptics-service.md +41 -0
- package/ai/reference/bf-harness-filters.md +29 -0
- package/ai/reference/bf-horizontal-scroll-directive.md +129 -0
- package/ai/reference/bf-icon-button-variant.md +28 -0
- package/ai/reference/bf-icon-size.md +27 -0
- package/ai/reference/bf-input-autocapitalize.md +36 -0
- package/ai/reference/bf-input-enter-key-hint.md +38 -0
- package/ai/reference/bf-input-input-mode.md +38 -0
- package/ai/reference/bf-input-mode.md +32 -0
- package/ai/reference/bf-input-type.md +35 -0
- package/ai/reference/bf-jsdom-shim-options.md +22 -0
- package/ai/reference/bf-keyboard-assist-behavior.md +26 -0
- package/ai/reference/bf-keyboard-assist-directive.md +185 -0
- package/ai/reference/bf-keyboard-phase.md +30 -0
- package/ai/reference/bf-keyboard-service.md +50 -0
- package/ai/reference/bf-labels-overrides.md +26 -0
- package/ai/reference/bf-labels.md +44 -0
- package/ai/reference/bf-menu-button-harness.md +40 -0
- package/ai/reference/bf-menu-button-variant.md +27 -0
- package/ai/reference/bf-menu-divider-directive.md +13 -0
- package/ai/reference/bf-menu-group-directive.md +14 -0
- package/ai/reference/bf-menu-harness.md +55 -0
- package/ai/reference/bf-menu-item-checkbox-directive.md +31 -0
- package/ai/reference/bf-menu-item-directive.md +31 -0
- package/ai/reference/bf-menu-item-harness.md +52 -0
- package/ai/reference/bf-menu-item-radio-directive.md +29 -0
- package/ai/reference/bf-menu-placement.md +29 -0
- package/ai/reference/bf-menu-trigger-directive.md +46 -0
- package/ai/reference/bf-modal-options.md +30 -0
- package/ai/reference/bf-modal-presentation.md +26 -0
- package/ai/reference/bf-modal-service.md +38 -0
- package/ai/reference/bf-nav-controller.md +59 -0
- package/ai/reference/bf-nav-direction.md +23 -0
- package/ai/reference/bf-nav-target.md +21 -0
- package/ai/reference/bf-number-input-mode.md +28 -0
- package/ai/reference/bf-number-input-step.md +25 -0
- package/ai/reference/bf-overlay-auto-focus.md +29 -0
- package/ai/reference/bf-overlay-controller.md +22 -0
- package/ai/reference/bf-overlay-host.md +36 -0
- package/ai/reference/bf-overlay-options.md +53 -0
- package/ai/reference/bf-overlay-position.md +30 -0
- package/ai/reference/bf-overlay-ref.md +53 -0
- package/ai/reference/bf-overlay-role.md +27 -0
- package/ai/reference/bf-overlay-service.md +53 -0
- package/ai/reference/bf-overlay-stack-options.md +24 -0
- package/ai/reference/bf-overlay-stack.md +81 -0
- package/ai/reference/bf-overlay-template-context.md +24 -0
- package/ai/reference/bf-overscroll-stretch-directive.md +163 -0
- package/ai/reference/bf-page-context.md +24 -0
- package/ai/reference/bf-platform-service.md +53 -0
- package/ai/reference/bf-platform.md +28 -0
- package/ai/reference/bf-progress-size.md +26 -0
- package/ai/reference/bf-progress-tone.md +28 -0
- package/ai/reference/bf-radio-group-harness.md +49 -0
- package/ai/reference/bf-radio-group-orientation.md +26 -0
- package/ai/reference/bf-radio-option.md +30 -0
- package/ai/reference/bf-refresher-refresh-event.md +22 -0
- package/ai/reference/bf-segment-option.md +29 -0
- package/ai/reference/bf-segmented-harness.md +48 -0
- package/ai/reference/bf-segmented-size.md +29 -0
- package/ai/reference/bf-select-harness.md +70 -0
- package/ai/reference/bf-select-option.md +29 -0
- package/ai/reference/bf-sheet-auto-focus.md +29 -0
- package/ai/reference/bf-sheet-options.md +32 -0
- package/ai/reference/bf-sheet-ref.md +45 -0
- package/ai/reference/bf-sheet-service.md +34 -0
- package/ai/reference/bf-shell-config-mode.md +28 -0
- package/ai/reference/bf-shell-config.md +38 -0
- package/ai/reference/bf-shell-dynamic-type-limit.md +21 -0
- package/ai/reference/bf-shell-dynamic-type.md +25 -0
- package/ai/reference/bf-shell-mode.md +28 -0
- package/ai/reference/bf-shell-testing-options.md +26 -0
- package/ai/reference/bf-size.md +45 -0
- package/ai/reference/bf-spinner-size.md +27 -0
- package/ai/reference/bf-stack-change.md +22 -0
- package/ai/reference/bf-stack-reuse-strategy.md +32 -0
- package/ai/reference/bf-stack-transition-event.md +26 -0
- package/ai/reference/bf-stack-transition.md +26 -0
- package/ai/reference/bf-stacked-overlay.md +23 -0
- package/ai/reference/bf-system-bars-style.md +38 -0
- package/ai/reference/bf-tab-bar-controller.md +51 -0
- package/ai/reference/bf-tab-bar-host.md +52 -0
- package/ai/reference/bf-tab-context.md +32 -0
- package/ai/reference/bf-tab-links.md +37 -0
- package/ai/reference/bf-testing-options.md +27 -0
- package/ai/reference/bf-text-scale-service.md +35 -0
- package/ai/reference/bf-theme.md +27 -0
- package/ai/reference/bf-toast-action.md +25 -0
- package/ai/reference/bf-toast-controller.md +17 -0
- package/ai/reference/bf-toast-dismiss-reason.md +29 -0
- package/ai/reference/bf-toast-options.md +29 -0
- package/ai/reference/bf-toast-position.md +26 -0
- package/ai/reference/bf-toast-ref.md +41 -0
- package/ai/reference/bf-toast-service.md +39 -0
- package/ai/reference/bf-toggle-label-position.md +26 -0
- package/ai/reference/bf-transition-kind.md +28 -0
- package/ai/reference/bf-validation-message.md +31 -0
- package/ai/reference/bf-validation-messages.md +33 -0
- package/ai/reference/bf-view-event.md +28 -0
- package/ai/reference/bf-view-lifecycle.md +41 -0
- package/ai/reference/bf-view-state.md +28 -0
- package/ai/reference/bf_default_labels.md +21 -0
- package/ai/reference/bf_default_values.md +21 -0
- package/ai/reference/bf_defaults.md +21 -0
- package/ai/reference/bf_desktop_toast_defaults.md +21 -0
- package/ai/reference/bf_dialog_data.md +17 -0
- package/ai/reference/bf_labels.md +21 -0
- package/ai/reference/bf_overlay_data.md +19 -0
- package/ai/reference/bf_overlay_host.md +21 -0
- package/ai/reference/bf_sheet_default_snap_points.md +17 -0
- package/ai/reference/bf_shell_config.md +21 -0
- package/ai/reference/bf_shell_defaults.md +21 -0
- package/ai/reference/bf_tab_bar.md +21 -0
- package/ai/reference/bf_tab_bar_controller.md +21 -0
- package/ai/reference/bf_tab_context.md +21 -0
- package/ai/reference/bf_tab_links.md +21 -0
- package/ai/reference/bf_theme_storage.md +19 -0
- package/ai/reference/create-transition.md +36 -0
- package/ai/reference/duration_fast.md +17 -0
- package/ai/reference/duration_md.md +17 -0
- package/ai/reference/duration_overlay.md +17 -0
- package/ai/reference/duration_push.md +17 -0
- package/ai/reference/fade-down.md +26 -0
- package/ai/reference/fade-in.md +25 -0
- package/ai/reference/fade-out.md +25 -0
- package/ai/reference/fade-up.md +26 -0
- package/ai/reference/inject-shell-mode.md +23 -0
- package/ai/reference/install-bf-jsdom-shims.md +31 -0
- package/ai/reference/ios_duration.md +17 -0
- package/ai/reference/ios_easing.md +17 -0
- package/ai/reference/is-text-entry.md +25 -0
- package/ai/reference/max_complete_ms.md +17 -0
- package/ai/reference/md-transition.md +31 -0
- package/ai/reference/md_duration.md +17 -0
- package/ai/reference/md_easing.md +17 -0
- package/ai/reference/overlay_duration.md +21 -0
- package/ai/reference/overlay_easing.md +19 -0
- package/ai/reference/overlay_reduced_duration.md +17 -0
- package/ai/reference/pop-frames-at.md +25 -0
- package/ai/reference/prefers-reduced-motion.md +25 -0
- package/ai/reference/provide-bf-defaults.md +33 -0
- package/ai/reference/provide-bf-desktop-toast.md +28 -0
- package/ai/reference/provide-bf-labels.md +38 -0
- package/ai/reference/provide-bf-shell-testing.md +34 -0
- package/ai/reference/provide-bf-shell.md +41 -0
- package/ai/reference/provide-bf-testing.md +33 -0
- package/ai/reference/reduced-transition.md +28 -0
- package/ai/reference/reduced_duration.md +17 -0
- package/ai/reference/resolve-shell-mode.md +33 -0
- package/ai/reference/scale-in.md +25 -0
- package/ai/reference/settle-duration.md +25 -0
- package/ai/reference/settle-pop.md +33 -0
- package/ai/reference/settle_easing.md +17 -0
- package/ai/reference/should-complete-pop.md +26 -0
- package/ai/reference/slide-down.md +26 -0
- package/ai/reference/slide-up.md +26 -0
- package/ai/reference/with-bf-shell-routing.md +19 -0
- package/bin/bf-ui.js +81 -0
- package/fesm2022/bitforgehq-ui-icons.mjs +230 -0
- package/fesm2022/bitforgehq-ui-icons.mjs.map +1 -0
- package/fesm2022/bitforgehq-ui-mobile.mjs +5530 -0
- package/fesm2022/bitforgehq-ui-mobile.mjs.map +1 -0
- package/fesm2022/bitforgehq-ui-number-input.mjs +680 -0
- package/fesm2022/bitforgehq-ui-number-input.mjs.map +1 -0
- package/fesm2022/bitforgehq-ui-overlay.mjs +2675 -0
- package/fesm2022/bitforgehq-ui-overlay.mjs.map +1 -0
- package/fesm2022/bitforgehq-ui-testing.mjs +402 -0
- package/fesm2022/bitforgehq-ui-testing.mjs.map +1 -0
- package/fesm2022/bitforgehq-ui.mjs +7765 -0
- package/fesm2022/bitforgehq-ui.mjs.map +1 -0
- package/llms-full.txt +25394 -0
- package/llms.txt +329 -0
- package/package.json +94 -0
- package/theme.css +253 -0
- package/types/bitforgehq-ui-icons.d.ts +50 -0
- package/types/bitforgehq-ui-mobile.d.ts +2914 -0
- package/types/bitforgehq-ui-number-input.d.ts +282 -0
- package/types/bitforgehq-ui-overlay.d.ts +1514 -0
- package/types/bitforgehq-ui-testing.d.ts +495 -0
- package/types/bitforgehq-ui.d.ts +3499 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# bf-empty-state
|
|
2
|
+
|
|
3
|
+
Placeholder for an empty screen or list with icon, title, message and action
|
|
4
|
+
|
|
5
|
+
`BfEmptyStateComponent` from `@bitforgehq/ui` · Feedback · since 0.1.0
|
|
6
|
+
|
|
7
|
+
## Example
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { Component, signal } from '@angular/core';
|
|
11
|
+
import { BfButtonComponent, BfEmptyStateComponent } from '@bitforgehq/ui';
|
|
12
|
+
import { BfIconComponent } from '@bitforgehq/ui/icons';
|
|
13
|
+
import { ListTodo } from 'lucide';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The bf-empty-state overview.
|
|
17
|
+
*
|
|
18
|
+
* Live demo of `bf-empty-state`: an icon tile, a title, a description and a primary action that counts the tasks it creates.
|
|
19
|
+
*/
|
|
20
|
+
@Component({
|
|
21
|
+
selector: 'app-example-empty-state-overview',
|
|
22
|
+
imports: [BfEmptyStateComponent, BfIconComponent, BfButtonComponent],
|
|
23
|
+
template: `
|
|
24
|
+
<div class="flex flex-col gap-3">
|
|
25
|
+
<bf-empty-state title="No tasks yet" description="Add your first task to get started.">
|
|
26
|
+
<bf-icon bfSlot="icon" [icon]="listTodo" size="lg" />
|
|
27
|
+
<bf-button bfSlot="actions" (clicked)="created.set(created() + 1)">New task</bf-button>
|
|
28
|
+
</bf-empty-state>
|
|
29
|
+
<p data-status aria-live="polite" class="text-caption text-text-muted text-center">
|
|
30
|
+
{{ created() === 0 ? 'No task created yet.' : 'Created ' + created() + ' task(s).' }}
|
|
31
|
+
</p>
|
|
32
|
+
</div>
|
|
33
|
+
`,
|
|
34
|
+
})
|
|
35
|
+
export class EmptyStateOverviewExampleComponent {
|
|
36
|
+
readonly listTodo = ListTodo;
|
|
37
|
+
readonly created = signal(0);
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Overview
|
|
42
|
+
|
|
43
|
+
Placeholder for a screen or list with nothing in it yet: an icon tile, a title, one sentence,
|
|
44
|
+
and the action that fills it.
|
|
45
|
+
|
|
46
|
+
An empty state fills the space where a list or screen would otherwise show nothing: an icon tile, a title, one sentence of explanation, and the action that gets the user unstuck.
|
|
47
|
+
|
|
48
|
+
## When to use
|
|
49
|
+
|
|
50
|
+
Use `bf-empty-state` where a list, a search result or a screen has nothing to show yet, with the one action that fills it. Use [bf-spinner](https://bitforge-ui.web.app/components/spinner) instead while the content is still loading, and [bf-alert](https://bitforge-ui.web.app/components/alert) when loading failed.
|
|
51
|
+
|
|
52
|
+
## Basic usage
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
import { Component } from '@angular/core';
|
|
56
|
+
import { BfEmptyStateComponent } from '@bitforgehq/ui';
|
|
57
|
+
|
|
58
|
+
/** An empty state with a title and a description. */
|
|
59
|
+
@Component({
|
|
60
|
+
selector: 'app-example-empty-state-basic',
|
|
61
|
+
imports: [BfEmptyStateComponent],
|
|
62
|
+
template: `
|
|
63
|
+
<bf-empty-state title="No tasks yet" description="Add your first task to get started." />
|
|
64
|
+
`,
|
|
65
|
+
})
|
|
66
|
+
export class EmptyStateBasicExampleComponent {}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## With an action
|
|
70
|
+
|
|
71
|
+
Put the icon in the `icon` slot (a `bf-icon` or any glyph) and the primary action in the `actions` slot; the empty state centers both for you.
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
import { Component, signal } from '@angular/core';
|
|
75
|
+
import { BfButtonComponent, BfEmptyStateComponent } from '@bitforgehq/ui';
|
|
76
|
+
import { BfIconComponent } from '@bitforgehq/ui/icons';
|
|
77
|
+
import { ListTodo } from 'lucide';
|
|
78
|
+
|
|
79
|
+
/** An empty state with an icon and an action. */
|
|
80
|
+
@Component({
|
|
81
|
+
selector: 'app-example-empty-state-action',
|
|
82
|
+
imports: [BfButtonComponent, BfEmptyStateComponent, BfIconComponent],
|
|
83
|
+
template: `
|
|
84
|
+
<bf-empty-state
|
|
85
|
+
title="No tasks yet"
|
|
86
|
+
[description]="created() ? 'Task created.' : 'Add your first task to get started.'"
|
|
87
|
+
>
|
|
88
|
+
<bf-icon bfSlot="icon" [icon]="listTodo" size="lg" />
|
|
89
|
+
<bf-button bfSlot="actions" (clicked)="created.set(true)">New task</bf-button>
|
|
90
|
+
</bf-empty-state>
|
|
91
|
+
`,
|
|
92
|
+
})
|
|
93
|
+
export class EmptyStateActionExampleComponent {
|
|
94
|
+
protected readonly listTodo = ListTodo;
|
|
95
|
+
protected readonly created = signal(false);
|
|
96
|
+
}
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Set `headingLevel` to `3` when the empty state sits inside a section that already opened with an `h2`, so the page outline stays in order.
|
|
100
|
+
|
|
101
|
+
## Theming
|
|
102
|
+
|
|
103
|
+
- Icon tile: `surface-sunken` background, `text-muted` icon color, `tile` radius.
|
|
104
|
+
- Title: `text`. Description: `text-muted`.
|
|
105
|
+
|
|
106
|
+
## Accessibility
|
|
107
|
+
|
|
108
|
+
The title renders as an `<h2>` by default and an `<h3>` when `headingLevel` is `3`, so it slots into the surrounding page outline instead of always claiming the top level. The icon and action slots carry their own accessibility — an icon should stay decorative, and the action needs its own accessible name.
|
|
109
|
+
|
|
110
|
+
## Inputs
|
|
111
|
+
|
|
112
|
+
| Name | Type | Default | Description |
|
|
113
|
+
| -------------- | ------------------------------------- | -------- | -------------------------------------------------------------------- |
|
|
114
|
+
| `title` | `string` | required | What is missing, as a short title. |
|
|
115
|
+
| `description` | `string` | `''` | One sentence on how to fill it. |
|
|
116
|
+
| `headingLevel` | `BfEmptyStateHeadingLevel` (`2 \| 3`) | `2` | Heading level of the title so the empty state fits the page outline. |
|
|
117
|
+
|
|
118
|
+
## Slots
|
|
119
|
+
|
|
120
|
+
- `icon`: The icon in the tile above the title.
|
|
121
|
+
- `actions`: Buttons under the description, such as Create.
|
|
122
|
+
|
|
123
|
+
## Notes
|
|
124
|
+
|
|
125
|
+
### Do
|
|
126
|
+
|
|
127
|
+
- Keep `description` to one sentence that says what to do next.
|
|
128
|
+
- Offer the action that resolves the empty state, not a link elsewhere.
|
|
129
|
+
|
|
130
|
+
### Don't
|
|
131
|
+
|
|
132
|
+
- Don't use `bf-empty-state` for a loading screen; that is `bf-spinner`.
|
|
133
|
+
|
|
134
|
+
## Migration notes
|
|
135
|
+
|
|
136
|
+
### 0.3.0
|
|
137
|
+
|
|
138
|
+
New exported `BfEmptyStateHeadingLevel` (`2 | 3`), the type of `headingLevel` (was an inline union
|
|
139
|
+
with the same values).
|
|
140
|
+
|
|
141
|
+
JSDoc of `empty-state.component.ts` for the API reference: `@slot` docs for `icon`, `actions`. No
|
|
142
|
+
behaviour change.
|
|
143
|
+
|
|
144
|
+
### 0.1.0
|
|
145
|
+
|
|
146
|
+
New `bf-empty-state`: a centered placeholder with a 64 px `icon` tile, a required `title`, an optional `description`, and an `actions` slot for the action that fills it. Added `headingLevel` (`2` default or `3`) so the title renders as `h2` or `h3` to fit the page outline.
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
# bf-field
|
|
2
|
+
|
|
3
|
+
Label, help text and error text wrapper that wires ids for assistive tech
|
|
4
|
+
|
|
5
|
+
`BfFieldComponent` from `@bitforgehq/ui` · Forms · since 0.1.0
|
|
6
|
+
|
|
7
|
+
## Example
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { Component, signal } from '@angular/core';
|
|
11
|
+
import { BfFieldComponent, bfFieldControlClass } from '@bitforgehq/ui';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The bf-field overview.
|
|
15
|
+
*
|
|
16
|
+
* Live demo of `bf-field`: wrapping a native `<input>` with a label and help text, styled by
|
|
17
|
+
* `bfFieldControlClass()` like `bf-input` and described by the field's help text; the typed value
|
|
18
|
+
* is echoed.
|
|
19
|
+
*/
|
|
20
|
+
@Component({
|
|
21
|
+
selector: 'app-example-field-overview',
|
|
22
|
+
imports: [BfFieldComponent],
|
|
23
|
+
template: `
|
|
24
|
+
<div class="flex flex-col gap-3">
|
|
25
|
+
<bf-field
|
|
26
|
+
#field
|
|
27
|
+
label="Project name"
|
|
28
|
+
helpText="Visible to your team"
|
|
29
|
+
controlId="field-demo-project"
|
|
30
|
+
>
|
|
31
|
+
<input
|
|
32
|
+
#project
|
|
33
|
+
id="field-demo-project"
|
|
34
|
+
placeholder="Atlas rebrand"
|
|
35
|
+
[class]="controlClass"
|
|
36
|
+
[attr.aria-describedby]="field.describedBy()"
|
|
37
|
+
(input)="name.set(project.value)"
|
|
38
|
+
/>
|
|
39
|
+
</bf-field>
|
|
40
|
+
<p data-status aria-live="polite" class="text-caption text-text-muted">
|
|
41
|
+
{{ name() ? 'Name: ' + name() : 'Type a project name.' }}
|
|
42
|
+
</p>
|
|
43
|
+
</div>
|
|
44
|
+
`,
|
|
45
|
+
})
|
|
46
|
+
export class FieldOverviewExampleComponent {
|
|
47
|
+
readonly name = signal('');
|
|
48
|
+
protected readonly controlClass = bfFieldControlClass({
|
|
49
|
+
size: 'md',
|
|
50
|
+
invalid: false,
|
|
51
|
+
disabled: false,
|
|
52
|
+
readonly: false,
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Overview
|
|
58
|
+
|
|
59
|
+
Label, help text and error text around a form control, with the id wiring assistive tech needs.
|
|
60
|
+
The form widgets use it internally; use it directly only to dress a custom control the same way.
|
|
61
|
+
|
|
62
|
+
A field wraps a control with its label, helper text and error text, and wires the ids that screen readers use. Every form widget in the library renders one internally; reach for it directly when you build a custom control and want it to look and behave like the others.
|
|
63
|
+
|
|
64
|
+
## When to use
|
|
65
|
+
|
|
66
|
+
Use `bf-field` around a control you build yourself, so it gets the library's label, help and error text and their ids. Use [bf-input](https://bitforge-ui.web.app/components/input), [bf-select](https://bitforge-ui.web.app/components/select) or [bf-textarea](https://bitforge-ui.web.app/components/textarea) instead for a standard field: each renders its own `bf-field`.
|
|
67
|
+
|
|
68
|
+
## Basic usage
|
|
69
|
+
|
|
70
|
+
Give the control an id and pass the same id as `controlId`. Style a native control with `bfFieldControlClass({ size, invalid, disabled, readonly })` from `@bitforgehq/ui` (`@since 0.3.0`): it returns the class list `bf-input` renders on its own `<input>` in the same state, so the custom control has the library's box, height per `BfSize` step, focus ring and danger border. Set the native `disabled` and `readonly` attributes on the control as well; their styles come from `:disabled` and `:read-only`. Take the field's template reference (`#field`) and bind the control's `aria-describedby` to `field.describedBy()`, and its `aria-invalid` to your own error state, so help and error text are announced.
|
|
71
|
+
|
|
72
|
+
```ts
|
|
73
|
+
import { Component, computed, inject, signal } from '@angular/core';
|
|
74
|
+
import { BF_DEFAULTS, BfFieldComponent, bfFieldControlClass } from '@bitforgehq/ui';
|
|
75
|
+
|
|
76
|
+
/** Keeps the control id unique when the example renders more than once. */
|
|
77
|
+
let nextId = 0;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* A custom control inside bf-field.
|
|
81
|
+
*
|
|
82
|
+
* A native `<input>` for a project code, styled by `bfFieldControlClass()` exactly like
|
|
83
|
+
* `bf-input` (size from `provideBfDefaults()`, the danger border once the code is invalid). The
|
|
84
|
+
* field's template reference hands `describedBy()` to the control's `aria-describedby`, so the
|
|
85
|
+
* help text, and after a wrong entry the error text, is announced. The code shows in capitals
|
|
86
|
+
* as it is typed (CSS `uppercase`, so the caret stays where it is) and is stored in capitals when
|
|
87
|
+
* the field is left.
|
|
88
|
+
*/
|
|
89
|
+
@Component({
|
|
90
|
+
selector: 'app-example-field-custom-control',
|
|
91
|
+
imports: [BfFieldComponent],
|
|
92
|
+
template: `
|
|
93
|
+
<bf-field
|
|
94
|
+
#field
|
|
95
|
+
label="Project code"
|
|
96
|
+
helpText="Three letters, a dash and three digits, e.g. ATL-204"
|
|
97
|
+
[error]="error()"
|
|
98
|
+
[controlId]="id"
|
|
99
|
+
>
|
|
100
|
+
<input
|
|
101
|
+
#control
|
|
102
|
+
[id]="id"
|
|
103
|
+
autocomplete="off"
|
|
104
|
+
spellcheck="false"
|
|
105
|
+
placeholder="ATL-204"
|
|
106
|
+
[value]="code()"
|
|
107
|
+
[class]="controlClass() + ' uppercase'"
|
|
108
|
+
[attr.aria-invalid]="error() ? 'true' : null"
|
|
109
|
+
[attr.aria-describedby]="field.describedBy()"
|
|
110
|
+
(input)="code.set(control.value)"
|
|
111
|
+
(blur)="leave(control.value)"
|
|
112
|
+
/>
|
|
113
|
+
</bf-field>
|
|
114
|
+
`,
|
|
115
|
+
})
|
|
116
|
+
export class FieldCustomControlExampleComponent {
|
|
117
|
+
private readonly size = inject(BF_DEFAULTS).fieldSize;
|
|
118
|
+
|
|
119
|
+
protected readonly id = `project-code-${nextId++}`;
|
|
120
|
+
protected readonly code = signal('');
|
|
121
|
+
protected readonly touched = signal(false);
|
|
122
|
+
protected readonly error = computed(() =>
|
|
123
|
+
this.touched() && !/^[A-Z]{3}-\d{3}$/i.test(this.code()) ? 'Enter a code like ATL-204' : '',
|
|
124
|
+
);
|
|
125
|
+
protected readonly controlClass = computed(() =>
|
|
126
|
+
bfFieldControlClass({
|
|
127
|
+
size: this.size,
|
|
128
|
+
invalid: this.error() !== '',
|
|
129
|
+
disabled: false,
|
|
130
|
+
readonly: false,
|
|
131
|
+
}),
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
/** Marks the field touched and stores the code in capitals, as it is shown. */
|
|
135
|
+
protected leave(value: string): void {
|
|
136
|
+
this.code.set(value.toUpperCase());
|
|
137
|
+
this.touched.set(true);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Error state
|
|
143
|
+
|
|
144
|
+
Setting `error` renders the error text in `danger` under the control; the control itself decides how it renders invalid (`bfFieldControlClass({ …, invalid: true })` gives it the danger border).
|
|
145
|
+
|
|
146
|
+
```ts
|
|
147
|
+
import { Component, signal } from '@angular/core';
|
|
148
|
+
import { BfFieldComponent, bfFieldControlClass } from '@bitforgehq/ui';
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* A field in its error state.
|
|
152
|
+
*
|
|
153
|
+
* `error` shows under the control in the danger colour; the control shows the danger border
|
|
154
|
+
* itself, here through `bfFieldControlClass({ invalid: true, … })`.
|
|
155
|
+
*/
|
|
156
|
+
@Component({
|
|
157
|
+
selector: 'app-example-field-error',
|
|
158
|
+
imports: [BfFieldComponent],
|
|
159
|
+
template: `
|
|
160
|
+
<bf-field #field label="Email" [error]="emailError()" controlId="field-error-email">
|
|
161
|
+
<input
|
|
162
|
+
id="field-error-email"
|
|
163
|
+
type="email"
|
|
164
|
+
value="lena@studio"
|
|
165
|
+
[class]="controlClass"
|
|
166
|
+
aria-invalid="true"
|
|
167
|
+
[attr.aria-describedby]="field.describedBy()"
|
|
168
|
+
/>
|
|
169
|
+
</bf-field>
|
|
170
|
+
`,
|
|
171
|
+
})
|
|
172
|
+
export class FieldErrorExampleComponent {
|
|
173
|
+
protected readonly emailError = signal('Enter a valid email address');
|
|
174
|
+
protected readonly controlClass = bfFieldControlClass({
|
|
175
|
+
size: 'md',
|
|
176
|
+
invalid: true,
|
|
177
|
+
disabled: false,
|
|
178
|
+
readonly: false,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## Help and error
|
|
184
|
+
|
|
185
|
+
The help text stays while an error shows: the error sits under it, so the hint the user needs to fix the value ("Letters and numbers only") never disappears at the moment it matters. `describedBy()` then lists both ids, help first (`"<controlId>-help <controlId>-error"`), so a screen reader reads the hint and the error.
|
|
186
|
+
|
|
187
|
+
An error that appears while the primary mouse button, a finger or a pen is held down is shown only once the press ends. A field is marked touched on blur, and a press on the form's Save button blurs it on mousedown: showing the message right then would push Save down by a line before the mouseup, and the click would be lost. During the press the message is already in the page, visually hidden, so the ids in `describedBy()` resolve and a screen reader reads it as soon as focus returns to the field; the same paragraph becomes visible a moment after the release, once the click has gone through. A key press or a pointer move with no button down shows it at once. An error that goes away during a press stays on screen until the release, for the same reason.
|
|
188
|
+
|
|
189
|
+
```ts
|
|
190
|
+
import { Component, signal } from '@angular/core';
|
|
191
|
+
import { FormField, FormRoot, form, pattern, required } from '@angular/forms/signals';
|
|
192
|
+
import { BfButtonComponent, BfInputComponent } from '@bitforgehq/ui';
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Help text that stays when the error shows.
|
|
196
|
+
*
|
|
197
|
+
* Press Save: the error appears under the help text, and the field is described by both.
|
|
198
|
+
*/
|
|
199
|
+
@Component({
|
|
200
|
+
selector: 'app-example-field-help-and-error',
|
|
201
|
+
imports: [FormField, FormRoot, BfButtonComponent, BfInputComponent],
|
|
202
|
+
template: `
|
|
203
|
+
<form [formRoot]="account" class="flex w-full flex-col gap-4 sm:w-80">
|
|
204
|
+
<bf-input
|
|
205
|
+
label="Username"
|
|
206
|
+
helpText="Letters and numbers only"
|
|
207
|
+
[formField]="account.username"
|
|
208
|
+
/>
|
|
209
|
+
<bf-button type="submit" class="self-start">Save</bf-button>
|
|
210
|
+
</form>
|
|
211
|
+
`,
|
|
212
|
+
})
|
|
213
|
+
export class FieldHelpAndErrorExampleComponent {
|
|
214
|
+
private readonly model = signal({ username: 'lena moser' });
|
|
215
|
+
protected readonly account = form(
|
|
216
|
+
this.model,
|
|
217
|
+
(p) => {
|
|
218
|
+
required(p.username);
|
|
219
|
+
pattern(p.username, /^[a-z0-9]+$/i, { message: 'Use letters and numbers only, no spaces' });
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
submission: {
|
|
223
|
+
action: () => Promise.resolve(undefined),
|
|
224
|
+
onInvalid: (f) => f().errorSummary()[0]?.fieldTree().focusBoundControl(),
|
|
225
|
+
},
|
|
226
|
+
},
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
## Theming
|
|
232
|
+
|
|
233
|
+
- Label: `text-muted`, switching to `primary-text` (primary as text, ≥ 4.5:1 on `bg` and `surface`) while the control inside is focused (`group-focus-within/bf-field`) and to `text-disabled` when `disabled`.
|
|
234
|
+
- Required marker: `danger`. Error text: `danger`. Help text: `text-muted`.
|
|
235
|
+
|
|
236
|
+
Label, control, help text and error text sit 6 px apart, the same label-to-message spacing as `bf-checkbox`, `bf-radio-group` and `bf-toggle`.
|
|
237
|
+
|
|
238
|
+
## Accessibility
|
|
239
|
+
|
|
240
|
+
The field renders the label as a native `<label for>` pointing at `controlId`, and gives the help and error paragraphs stable ids (`<controlId>-help` / `<controlId>-error`); both render together, the error under the help text. Label, help and error text break long words anywhere (`overflow-wrap: anywhere`), so a German or Finnish compound never runs past the field at 320 px or 400 % zoom. It sets no ARIA on the control itself — bind the projected control's `aria-describedby` to `describedBy()` (and its own `aria-invalid`, plus `aria-errormessage` pointing at `<controlId>-error` while it is invalid, as the library's controls do) so the wiring actually reaches assistive tech.
|
|
241
|
+
|
|
242
|
+
## Inputs
|
|
243
|
+
|
|
244
|
+
| Name | Type | Default | Description |
|
|
245
|
+
| ----------- | --------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
246
|
+
| `label` | `string` | `''` | Visible label; rendered as a `<label for>` pointing at `controlId`. |
|
|
247
|
+
| `helpText` | `string` | `''` | Helper text under the control; stays while `error` is shown. |
|
|
248
|
+
| `error` | `string` | `''` | Error text under the help text. One that appears while a pointer button is held down (the press on a Save button that blurred the field) renders visually hidden until the press ends, so assistive tech has it at once and the layout never moves under the pointer between mousedown and mouseup. |
|
|
249
|
+
| `required` | `boolean` | `false` | Adds the required marker to the label. |
|
|
250
|
+
| `disabled` | `boolean` | `false` | Dims the label; the control carries its own disabled styling. |
|
|
251
|
+
| `controlId` | `string` | required | The id of the control inside the default slot. |
|
|
252
|
+
|
|
253
|
+
## Slots
|
|
254
|
+
|
|
255
|
+
- `default`: The control the field labels, such as a native input styled with `bfFieldControlClass`.
|
|
256
|
+
|
|
257
|
+
## Notes
|
|
258
|
+
|
|
259
|
+
### Do
|
|
260
|
+
|
|
261
|
+
- Always give a control a visible label; use `placeholder` only as an example value.
|
|
262
|
+
- Keep help text to one short sentence.
|
|
263
|
+
|
|
264
|
+
### Don't
|
|
265
|
+
|
|
266
|
+
- Don't nest one `bf-field` inside another.
|
|
267
|
+
- Don't use it for buttons or read-only text.
|
|
268
|
+
|
|
269
|
+
## Migration notes
|
|
270
|
+
|
|
271
|
+
### 0.3.0
|
|
272
|
+
|
|
273
|
+
**Behaviour:** the label of a focused field is `primary-text` (was `primary`).
|
|
274
|
+
|
|
275
|
+
New export `bfFieldControlClass(state: { size: BfSize; invalid: boolean; disabled: boolean;
|
|
276
|
+
readonly: boolean }): string` (and its `BfFieldControlState` type) from `@bitforgehq/ui`: the class
|
|
277
|
+
list `bf-input` renders on its native `<input>` in the same state (box, height and type per size,
|
|
278
|
+
padding, focus ring, `border-strong` or, while `invalid`, `danger-border`), so a custom control
|
|
279
|
+
inside `bf-field` looks like the library's own fields without copying its classes. The disabled
|
|
280
|
+
and read-only styles come from the control's native `disabled`/`readonly` attributes. `bf-input`
|
|
281
|
+
builds its own list from the same pieces, and a spec asserts the two agree for every size and
|
|
282
|
+
state. The field docs and the `@example` bind the control's `aria-describedby` to the field's
|
|
283
|
+
`describedBy()` through a template reference (`#field`); the new `field/custom-control` example
|
|
284
|
+
does so for a native input styled by `bfFieldControlClass()`.
|
|
285
|
+
|
|
286
|
+
JSDoc of `field-classes.ts` for the API reference: `@summary`/`@since` on the exported symbols;
|
|
287
|
+
`@param` docs. No behaviour change.
|
|
288
|
+
|
|
289
|
+
JSDoc of `field.component.ts` for the API reference: `@slot` docs for `default`. No behaviour
|
|
290
|
+
change.
|
|
291
|
+
|
|
292
|
+
**Behaviour:** the help paragraph renders always (when set) and the error paragraph under it;
|
|
293
|
+
help text no longer disappears on error (F-020). `describedBy()` lists both ids,
|
|
294
|
+
`"<controlId>-help <controlId>-error"`, `"<controlId>-help"`, `"<controlId>-error"` or `null`. A new
|
|
295
|
+
`error` that arrives while a pointer button is held down anywhere in the page shows one task after
|
|
296
|
+
the release (or at once on a key press, or a pointer move with no button down): the press on a
|
|
297
|
+
Save button blurs the field and marks it touched on mousedown, and the message pushed Save about
|
|
298
|
+
22 px down before the mouseup, so the click was lost in every browser. The field holds its layout
|
|
299
|
+
through the press instead of reserving an empty line under every field. See Migration 25.
|
|
300
|
+
|
|
301
|
+
`fieldDescribedBy(id, errorText, helpText)` (internal, `ɵfieldDescribedBy`) returns
|
|
302
|
+
`"<id>-help <id>-error"`, `"<id>-help"`, `"<id>-error"` or `null`; the new internal
|
|
303
|
+
`ɵfieldErrorMessageId` gives the controls' `aria-errormessage`.
|
|
304
|
+
|
|
305
|
+
While a press holds a new error back, its paragraph is already rendered, visually hidden
|
|
306
|
+
(`sr-only`), so the controls' `aria-describedby` and `aria-errormessage` always point at an element
|
|
307
|
+
that exists and assistive tech reads the error when focus returns to the field; the same paragraph
|
|
308
|
+
turns visible on release. Only the primary mouse button, a touch or a pen starts a hold (a
|
|
309
|
+
right-click never does). The self-rendering controls (checkbox, toggle, radio group, segmented)
|
|
310
|
+
behave the same way.
|
|
311
|
+
|
|
312
|
+
### 0.2.0
|
|
313
|
+
|
|
314
|
+
Fixed: the label, help and error text break long words anywhere (`wrap-anywhere`), so a German or
|
|
315
|
+
Finnish compound no longer runs past the field at 320 px.
|
|
316
|
+
|
|
317
|
+
### 0.1.0
|
|
318
|
+
|
|
319
|
+
`disabled` no longer dims the whole field; it dims only the label. Controls already carry their own disabled styling.
|