@bitforgehq/ui 0.3.0 → 0.3.1
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 +482 -0
- package/README.md +17 -11
- package/ai/MANIFEST.index.json +288 -0
- package/ai/MANIFEST.json +8716 -3926
- package/ai/REFERENCE.index.json +413 -0
- package/ai/REFERENCE.json +4710 -2047
- package/ai/UI_GUIDE.md +185 -57
- package/ai/components/back-button.md +42 -2
- package/ai/components/badge.md +17 -7
- package/ai/components/bar-chart.md +188 -0
- package/ai/components/breadcrumb-item.md +121 -0
- package/ai/components/breadcrumb.md +277 -0
- package/ai/components/button.md +82 -15
- package/ai/components/calendar.md +300 -0
- package/ai/components/card.md +63 -2
- package/ai/components/chart-card.md +283 -0
- package/ai/components/combobox.md +411 -0
- package/ai/components/date-input.md +396 -0
- package/ai/components/datetime-input.md +215 -0
- package/ai/components/description-item.md +111 -0
- package/ai/components/description-list.md +160 -0
- package/ai/components/donut-chart.md +169 -0
- package/ai/components/header.md +67 -2
- package/ai/components/icon-button.md +74 -16
- package/ai/components/icon.md +63 -7
- package/ai/components/line-chart.md +234 -0
- package/ai/components/list.md +70 -6
- package/ai/components/menu.md +5 -0
- package/ai/components/multi-combobox.md +257 -0
- package/ai/components/nav-group.md +178 -0
- package/ai/components/nav-item.md +210 -0
- package/ai/components/page-header.md +237 -0
- package/ai/components/pagination.md +173 -0
- package/ai/components/popover.md +250 -0
- package/ai/components/side-panel.md +391 -0
- package/ai/components/sidebar.md +363 -0
- package/ai/components/skeleton.md +215 -0
- package/ai/components/sparkline.md +188 -0
- package/ai/components/stat.md +233 -0
- package/ai/components/tab-group.md +226 -0
- package/ai/components/tab-panel.md +163 -0
- package/ai/components/table.md +568 -0
- package/ai/components/tabs.md +42 -2
- package/ai/components/theme-menu-button.md +182 -0
- package/ai/components/theme-switch.md +170 -0
- package/ai/components/time-input.md +240 -0
- package/ai/components/toolbar.md +289 -0
- package/ai/components/tooltip.md +230 -0
- package/ai/components/top-nav.md +159 -0
- package/ai/reference/bf-badge-appearance.md +36 -0
- package/ai/reference/bf-badge-variant.md +1 -1
- package/ai/reference/bf-bar-chart-orientation.md +27 -0
- package/ai/reference/bf-button-appearance.md +36 -0
- package/ai/reference/bf-button-variant.md +1 -1
- package/ai/reference/bf-calendar-disabled-date.md +23 -0
- package/ai/reference/bf-calendar-harness.md +51 -0
- package/ai/reference/bf-calendar-weekday.md +31 -0
- package/ai/reference/bf-chart-card-heading-level.md +26 -0
- package/ai/reference/bf-chart-curve.md +28 -0
- package/ai/reference/bf-chart-harness.md +51 -0
- package/ai/reference/bf-chart-legend-position.md +30 -0
- package/ai/reference/bf-chart-series.md +23 -0
- package/ai/reference/bf-chart-value-format.md +23 -0
- package/ai/reference/bf-color-scheme-service.md +30 -0
- package/ai/reference/bf-color-scheme.md +31 -0
- package/ai/reference/bf-combobox-filter.md +27 -0
- package/ai/reference/bf-combobox-harness.md +89 -0
- package/ai/reference/bf-combobox-option.md +33 -0
- package/ai/reference/bf-compare-with.md +2 -0
- package/ai/reference/bf-date-input-harness.md +67 -0
- package/ai/reference/bf-date-input-picker.md +27 -0
- package/ai/reference/bf-date-time-input-harness.md +76 -0
- package/ai/reference/bf-date-time-input-picker.md +27 -0
- package/ai/reference/bf-description-list-layout.md +26 -0
- package/ai/reference/bf-donut-slice.md +22 -0
- package/ai/reference/bf-labels.md +67 -20
- package/ai/reference/bf-menu-button-variant.md +1 -0
- package/ai/reference/bf-menu-placement.md +1 -0
- package/ai/reference/bf-overlay-options.md +28 -27
- package/ai/reference/bf-overlay-placement.md +34 -0
- package/ai/reference/bf-overlay-position.md +1 -1
- package/ai/reference/bf-overlay-service.md +5 -3
- package/ai/reference/bf-page-header-heading-level.md +26 -0
- package/ai/reference/bf-pagination-harness.md +61 -0
- package/ai/reference/bf-platform-service.md +17 -15
- package/ai/reference/bf-popover-harness.md +42 -0
- package/ai/reference/bf-popover-placement.md +28 -0
- package/ai/reference/bf-popover-trigger-directive.md +56 -0
- package/ai/reference/bf-side-panel-config.md +35 -0
- package/ai/reference/bf-side-panel-harness.md +34 -0
- package/ai/reference/bf-side-panel-ref.md +44 -0
- package/ai/reference/bf-side-panel-service.md +43 -0
- package/ai/reference/bf-side-panel-template-context.md +32 -0
- package/ai/reference/bf-side-panel-width.md +28 -0
- package/ai/reference/bf-size.md +12 -0
- package/ai/reference/bf-skeleton-shape.md +28 -0
- package/ai/reference/bf-stat-trend-intent.md +27 -0
- package/ai/reference/bf-stat-trend.md +27 -0
- package/ai/reference/bf-stat-variant.md +26 -0
- package/ai/reference/bf-tab-group-harness.md +44 -0
- package/ai/reference/bf-table-align.md +26 -0
- package/ai/reference/bf-table-cell-context.md +24 -0
- package/ai/reference/bf-table-cell-directive.md +22 -0
- package/ai/reference/bf-table-column.md +28 -0
- package/ai/reference/bf-table-harness.md +77 -0
- package/ai/reference/bf-table-row-key.md +21 -0
- package/ai/reference/bf-table-sort-direction.md +26 -0
- package/ai/reference/bf-table-sort.md +27 -0
- package/ai/reference/bf-theme.md +5 -4
- package/ai/reference/bf-time-input-harness.md +67 -0
- package/ai/reference/bf-time-input-hour-cycle.md +27 -0
- package/ai/reference/bf-time-input-picker.md +27 -0
- package/ai/reference/bf-tooltip-directive.md +43 -0
- package/ai/reference/bf-tooltip-harness.md +43 -0
- package/ai/reference/bf-tooltip-placement.md +28 -0
- package/ai/reference/bf_color_scheme_storage.md +20 -0
- package/ai/reference/bf_side_panel_data.md +17 -0
- package/ai/reference/bf_theme_storage.md +3 -1
- package/ai/reference/provide-bf-color-scheme.md +20 -0
- package/ai/reference/provide-bf-labels.md +12 -4
- package/ai/reference/slide-in-from-end.md +24 -0
- package/ai/reference/slide-out-to-end.md +24 -0
- package/fesm2022/bitforgehq-ui-charts.mjs +3045 -0
- package/fesm2022/bitforgehq-ui-charts.mjs.map +1 -0
- package/fesm2022/bitforgehq-ui-icons.mjs +107 -3
- package/fesm2022/bitforgehq-ui-icons.mjs.map +1 -1
- package/fesm2022/bitforgehq-ui-mobile.mjs +14 -59
- package/fesm2022/bitforgehq-ui-mobile.mjs.map +1 -1
- package/fesm2022/bitforgehq-ui-overlay.mjs +6436 -33
- package/fesm2022/bitforgehq-ui-overlay.mjs.map +1 -1
- package/fesm2022/bitforgehq-ui-testing.mjs +618 -1
- package/fesm2022/bitforgehq-ui-testing.mjs.map +1 -1
- package/fesm2022/bitforgehq-ui.mjs +5092 -153
- package/fesm2022/bitforgehq-ui.mjs.map +1 -1
- package/llms-full.txt +25177 -14398
- package/llms.txt +95 -1
- package/package.json +7 -3
- package/theme.css +101 -0
- package/types/bitforgehq-ui-charts.d.ts +967 -0
- package/types/bitforgehq-ui-icons.d.ts +13 -1
- package/types/bitforgehq-ui-mobile.d.ts +25 -18
- package/types/bitforgehq-ui-overlay.d.ts +1844 -7
- package/types/bitforgehq-ui-testing.d.ts +748 -1
- package/types/bitforgehq-ui.d.ts +2136 -44
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
# bf-multi-combobox
|
|
2
|
+
|
|
3
|
+
Text field that filters options as you type and picks several as tags
|
|
4
|
+
|
|
5
|
+
`BfMultiComboboxComponent` from `@bitforgehq/ui/overlay` · Forms · since 0.3.1
|
|
6
|
+
|
|
7
|
+
## Example
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { Component, computed, signal } from '@angular/core';
|
|
11
|
+
import { BfMultiComboboxComponent, type BfComboboxOption } from '@bitforgehq/ui/overlay';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The bf-multi-combobox overview.
|
|
15
|
+
*
|
|
16
|
+
* Live demo of `bf-multi-combobox`: tag a task with labels. Typing filters the labels, `Enter` or
|
|
17
|
+
* a click adds the active one as a tag (or removes it when it is a tag already) and the list
|
|
18
|
+
* stays open for the next; each tag's button removes it, and `Backspace` in the empty field
|
|
19
|
+
* removes the last one. The result line lists the labels.
|
|
20
|
+
*/
|
|
21
|
+
@Component({
|
|
22
|
+
selector: 'app-example-multi-combobox-overview',
|
|
23
|
+
imports: [BfMultiComboboxComponent],
|
|
24
|
+
template: `
|
|
25
|
+
<div class="flex max-w-md flex-col gap-3">
|
|
26
|
+
<bf-multi-combobox
|
|
27
|
+
label="Labels"
|
|
28
|
+
placeholder="Add labels"
|
|
29
|
+
[options]="labels"
|
|
30
|
+
[(value)]="picked"
|
|
31
|
+
/>
|
|
32
|
+
<p data-status aria-live="polite" class="text-caption text-text-muted">{{ status() }}</p>
|
|
33
|
+
</div>
|
|
34
|
+
`,
|
|
35
|
+
})
|
|
36
|
+
export class MultiComboboxOverviewExampleComponent {
|
|
37
|
+
protected readonly labels: readonly BfComboboxOption[] = [
|
|
38
|
+
{ value: 'bug', label: 'Bug' },
|
|
39
|
+
{ value: 'design', label: 'Design' },
|
|
40
|
+
{ value: 'docs', label: 'Documentation' },
|
|
41
|
+
{ value: 'perf', label: 'Performance' },
|
|
42
|
+
{ value: 'a11y', label: 'Accessibility' },
|
|
43
|
+
{ value: 'security', label: 'Security' },
|
|
44
|
+
];
|
|
45
|
+
readonly picked = signal<readonly string[]>(['design']);
|
|
46
|
+
|
|
47
|
+
readonly status = computed(() => {
|
|
48
|
+
const names = this.labels.filter((l) => this.picked().includes(l.value)).map((l) => l.label);
|
|
49
|
+
return names.length ? `Labels: ${names.join(', ')}.` : 'No labels.';
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
## Overview
|
|
55
|
+
|
|
56
|
+
Text field with a filtered list of options to pick several values from, shown as removable tags
|
|
57
|
+
inside the field (the ARIA combobox pattern with a multi-select listbox). Typing filters the
|
|
58
|
+
list by label, ignoring case and accents; `Enter` or a click adds or removes the active option
|
|
59
|
+
and the list stays open. Generic over the option value (a string by default); the value is an
|
|
60
|
+
array, empty while nothing is picked. For one value use `bf-combobox`.
|
|
61
|
+
|
|
62
|
+
A text field with a list of options that filters as you type, to pick several values, shown as
|
|
63
|
+
removable tags inside the field: labels for a task, reviewers, skills. The same
|
|
64
|
+
[ARIA 1.2 combobox](https://www.w3.org/WAI/ARIA/apg/patterns/combobox/) pattern as `bf-combobox`,
|
|
65
|
+
with a multi-select listbox: the list stays open after each pick, and picked options carry a check
|
|
66
|
+
mark. Its value is an array (`readonly T[]`, empty while nothing is picked), bound with
|
|
67
|
+
`[(value)]` or `[formField]`.
|
|
68
|
+
|
|
69
|
+
Import `BfMultiComboboxComponent` and `BfComboboxOption` from `@bitforgehq/ui/overlay` (needs
|
|
70
|
+
`@angular/cdk`).
|
|
71
|
+
|
|
72
|
+
## When to use
|
|
73
|
+
|
|
74
|
+
Use `bf-multi-combobox` to pick any number of values from a list long enough to search. Use
|
|
75
|
+
[bf-combobox](https://bitforge-ui.web.app/components/combobox) instead for one value, [bf-chip](https://bitforge-ui.web.app/components/chip) filter
|
|
76
|
+
chips for a few options shown all at once, and checkboxes in a
|
|
77
|
+
[bf-popover](https://bitforge-ui.web.app/components/popover) for a short list of filters. Both work in both shells; in a
|
|
78
|
+
Capacitor app set `provideBfDefaults({ fieldSize: 'lg' })` for 48 px fields.
|
|
79
|
+
|
|
80
|
+
## Tags
|
|
81
|
+
|
|
82
|
+
Each picked value is a tag before the text, in the order it was picked. A tag's button (named
|
|
83
|
+
"Remove …" by the `comboboxRemove` label) removes it and puts focus back in the field, and
|
|
84
|
+
`Backspace` in the empty field removes the last tag. `(changed)` emits the new array after each
|
|
85
|
+
add and remove. The field grows in height as the tags wrap, and the open list follows its bottom
|
|
86
|
+
edge. While `disabled` or `readonly` the tags have no remove buttons.
|
|
87
|
+
|
|
88
|
+
## Typing and picking
|
|
89
|
+
|
|
90
|
+
Typing filters the options by label, ignoring case and accents, and opens the list. `Enter` or a
|
|
91
|
+
click on the active option adds it, or removes it when it is a tag already; the typed text clears
|
|
92
|
+
and the list stays open for the next pick. `Escape` closes the list, a second `Escape` drops the
|
|
93
|
+
typed text, and leaving the field drops it too. Options take a `description`, `disabled` and
|
|
94
|
+
`group` (listed under a heading), as on `bf-combobox`.
|
|
95
|
+
|
|
96
|
+
`filter`, `(queried)`, `loading` and `[filter]="null"` work as on `bf-combobox` for a server
|
|
97
|
+
search; the field keeps the labels of picked values that later results no longer hold.
|
|
98
|
+
|
|
99
|
+
## With Signal Forms
|
|
100
|
+
|
|
101
|
+
Model the field as `readonly T[]`, starting at `[]`, and bind it with `[formField]`. `placeholder`
|
|
102
|
+
shows while nothing is picked. For object values pass `compareWith`, as on `bf-combobox`.
|
|
103
|
+
|
|
104
|
+
```ts
|
|
105
|
+
import { Component, signal } from '@angular/core';
|
|
106
|
+
import { FormField, form } from '@angular/forms/signals';
|
|
107
|
+
import { BfMultiComboboxComponent, type BfComboboxOption } from '@bitforgehq/ui/overlay';
|
|
108
|
+
|
|
109
|
+
interface Person {
|
|
110
|
+
id: number;
|
|
111
|
+
name: string;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* A multi combobox bound to a Signal Forms field.
|
|
116
|
+
*
|
|
117
|
+
* The field holds an array of objects, starting with one reviewer; `compareWith` matches them to
|
|
118
|
+
* the options by id, so reviewers reloaded from a server still show as tags and check marks.
|
|
119
|
+
*/
|
|
120
|
+
@Component({
|
|
121
|
+
selector: 'app-example-multi-combobox-forms',
|
|
122
|
+
imports: [BfMultiComboboxComponent, FormField],
|
|
123
|
+
template: `
|
|
124
|
+
<bf-multi-combobox
|
|
125
|
+
class="max-w-md"
|
|
126
|
+
label="Reviewers"
|
|
127
|
+
placeholder="Add reviewers"
|
|
128
|
+
helpText="Each reviewer gets a notification."
|
|
129
|
+
[options]="people"
|
|
130
|
+
[compareWith]="sameId"
|
|
131
|
+
[formField]="review.reviewers"
|
|
132
|
+
/>
|
|
133
|
+
`,
|
|
134
|
+
})
|
|
135
|
+
export class MultiComboboxFormsExampleComponent {
|
|
136
|
+
protected readonly people: readonly BfComboboxOption<Person>[] = [
|
|
137
|
+
{ value: { id: 1, name: 'Ada' }, label: 'Ada Lovelace' },
|
|
138
|
+
{ value: { id: 2, name: 'Grace' }, label: 'Grace Hopper' },
|
|
139
|
+
{ value: { id: 3, name: 'Katherine' }, label: 'Katherine Johnson' },
|
|
140
|
+
];
|
|
141
|
+
protected readonly sameId = (a: Person, b: Person): boolean => a.id === b.id;
|
|
142
|
+
private readonly model = signal<{ reviewers: readonly Person[] }>({
|
|
143
|
+
reviewers: [{ id: 2, name: 'Grace' }],
|
|
144
|
+
});
|
|
145
|
+
protected readonly review = form(this.model);
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Desktop
|
|
150
|
+
|
|
151
|
+
The field keeps focus while the arrow keys move through the list; the pointer moving over an
|
|
152
|
+
option makes it the active one. A press anywhere on the field's box (around the tags) puts the
|
|
153
|
+
caret in the field and opens the list. Under a fine pointer a tag's remove button is 24 px; under a
|
|
154
|
+
coarse pointer its hit area grows to 44 px and every option row is at least 44 px tall.
|
|
155
|
+
|
|
156
|
+
## Theming
|
|
157
|
+
|
|
158
|
+
- Field: the `bf-input` tokens on the box around the tags: `border-strong` border
|
|
159
|
+
(`danger-border` on error), `primary` border and `ring` ring while the field has focus,
|
|
160
|
+
`surface-muted` while read-only or disabled, a `text-subtle` chevron.
|
|
161
|
+
- Tags: `primary-soft` with `primary-strong` text (the selected tonal chip's colours);
|
|
162
|
+
the remove button hovers `primary-soft-hover` and presses `primary-soft-pressed`.
|
|
163
|
+
- List: as on `bf-combobox`, with a check mark on each picked option.
|
|
164
|
+
|
|
165
|
+
## Accessibility
|
|
166
|
+
|
|
167
|
+
- The input is `role="combobox"` with `aria-autocomplete="list"`, `aria-expanded`,
|
|
168
|
+
`aria-controls` and `aria-activedescendant`, as on `bf-combobox`; the listbox has
|
|
169
|
+
`aria-multiselectable="true"` and every picked option `aria-selected="true"`.
|
|
170
|
+
- Each tag's remove button is a native button named "Remove" plus the tag's label, in the tab
|
|
171
|
+
order before the input.
|
|
172
|
+
- Labels, help and error text, `aria-describedby`, `aria-invalid` and `aria-errormessage` follow
|
|
173
|
+
`bf-select`. `required` sets the native attribute only while no tag is picked.
|
|
174
|
+
|
|
175
|
+
### Keyboard
|
|
176
|
+
|
|
177
|
+
| Key | Where | Action |
|
|
178
|
+
| ---------------------------------------------------- | ------------- | --------------------------------------------------------------- |
|
|
179
|
+
| <kbd>ArrowDown</kbd> / <kbd>ArrowUp</kbd> | Field | Opens the list on the first tag's option (else first / last) |
|
|
180
|
+
| <kbd>ArrowDown</kbd> / <kbd>ArrowUp</kbd> | Open list | Next / previous enabled option, wrapping at the ends |
|
|
181
|
+
| <kbd>Home</kbd> / <kbd>End</kbd> | Open list | First / last enabled option |
|
|
182
|
+
| <kbd>Enter</kbd> | Open list | Adds the active option, or removes it; the list stays open |
|
|
183
|
+
| <kbd>Backspace</kbd> | Empty field | Removes the last tag |
|
|
184
|
+
| <kbd>Escape</kbd> | Open list | Closes the list |
|
|
185
|
+
| <kbd>Escape</kbd> | Field | Drops the typed text |
|
|
186
|
+
| <kbd>Tab</kbd> / <kbd>Shift</kbd>+<kbd>Tab</kbd> | Field | Moves on (or back to the tags' remove buttons); the list closes |
|
|
187
|
+
| <kbd>Enter</kbd> / <kbd>Space</kbd> | Remove button | Removes that tag |
|
|
188
|
+
| <kbd>A</kbd>–<kbd>Z</kbd>, <kbd>0</kbd>–<kbd>9</kbd> | Field | Filters the list and opens it |
|
|
189
|
+
|
|
190
|
+
## Inputs
|
|
191
|
+
|
|
192
|
+
| Name | Type | Default | Description |
|
|
193
|
+
| ---------------- | ----------------------------------------------------------- | --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
194
|
+
| `options` | `readonly BfComboboxOption<T>[]` | required | The options to choose from; with server search, the latest results. |
|
|
195
|
+
| `compareWith` | `BfCompareWith<T>` | `Object.is` | Equality for matching `value` to an option (default `Object.is`); pass one for object values. |
|
|
196
|
+
| `filter` | `BfComboboxFilter<T> \| null` | `labelContains` | Whether an option matches the typed text (default: its label contains it, ignoring case and accents); `null` shows `options` unfiltered, for results filtered by a server. |
|
|
197
|
+
| `size` | `BfSize \| undefined` (`'sm' \| 'md' \| 'lg' \| undefined`) | `undefined` | Height step of the field (its minimum height): sm 32, md 40, lg 48 px; unset follows `provideBfDefaults()` (`fieldSize`, `md` by default). |
|
|
198
|
+
| `label` | `string` | `''` | Visible label above the field. |
|
|
199
|
+
| `ariaLabel` | `string` | `''` | Accessible name when no visible label is rendered. Ignored when `label` is set. |
|
|
200
|
+
| `ariaLabelledby` | `string` | `''` | Id(s) of the element(s) that name the field when no visible label is rendered. Ignored when `label` is set. |
|
|
201
|
+
| `placeholder` | `string` | `''` | Hint text shown while nothing is picked. |
|
|
202
|
+
| `helpText` | `string` | `''` | Helper text under the field. |
|
|
203
|
+
| `errorMessage` | `string` | `''` | Explicit error text; takes precedence over Signal Forms errors. |
|
|
204
|
+
| `emptyLabel` | `string` | `''` | Text of the list's row when no option matches; empty uses the `comboboxEmpty` label. |
|
|
205
|
+
| `loading` | `boolean` | `false` | Shows a loading row in the list, while a server search for the typed text runs. |
|
|
206
|
+
| `disabled` | `boolean` | `false` | Disables the field and its tags. Set by `[formField]`; do not bind it yourself. |
|
|
207
|
+
| `readonly` | `boolean` | `false` | Shows the tags but blocks changes: no remove buttons, and the list never opens. Set by `[formField]`; do not bind it yourself. |
|
|
208
|
+
| `required` | `boolean` | `false` | Marks the field required (at least one value). Set by `[formField]`; do not bind it yourself. |
|
|
209
|
+
| `invalid` | `boolean` | `false` | Field validity from Signal Forms. Set by `[formField]`; do not bind it yourself. |
|
|
210
|
+
| `touched` | `boolean` | `false` | Whether the user has left the field. Set by `[formField]`; do not bind it yourself. |
|
|
211
|
+
| `errors` | `readonly ValidationError.WithOptionalFieldTree[]` | `[]` | Validation errors from Signal Forms. Set by `[formField]`; do not bind it yourself. |
|
|
212
|
+
| `name` | `string` | `''` | Native name attribute of the text input. Set by `[formField]`; do not bind it yourself. |
|
|
213
|
+
| `id` | `string` | `generated` | Input id; generated when not set. |
|
|
214
|
+
| `value` | `readonly T[]` | `[]` | The picked options' values in the order they were picked, empty while nothing is picked; two-way bindable and bound by `[formField]`. Two-way: `[(value)]`. |
|
|
215
|
+
|
|
216
|
+
## Outputs
|
|
217
|
+
|
|
218
|
+
| Name | Payload | Description |
|
|
219
|
+
| --------- | -------------- | --------------------------------------------------------------------------------------------------------- |
|
|
220
|
+
| `changed` | `readonly T[]` | Emitted with the new values after the user adds or removes one. |
|
|
221
|
+
| `queried` | `string` | Emitted with the typed text each time the user changes it (`''` once it is cleared), for a server search. |
|
|
222
|
+
| `focused` | `FocusEvent` | Emitted when the input gains focus. |
|
|
223
|
+
| `blurred` | `FocusEvent` | Emitted when the input loses focus. |
|
|
224
|
+
| `touch` | `void` | Emitted on blur so Signal Forms marks the field touched. Set by `[formField]`; do not bind it yourself. |
|
|
225
|
+
|
|
226
|
+
## Methods
|
|
227
|
+
|
|
228
|
+
### `focus(options?: FocusOptions): void`
|
|
229
|
+
|
|
230
|
+
Focuses the input; Signal Forms calls it from `focusBoundControl()`.
|
|
231
|
+
|
|
232
|
+
| Parameter | Type | Description |
|
|
233
|
+
| ---------- | -------------- | --------------------------------------------------------------------- |
|
|
234
|
+
| `options?` | `FocusOptions` | Passed on to the native `focus()`, such as `{ preventScroll: true }`. |
|
|
235
|
+
|
|
236
|
+
## Notes
|
|
237
|
+
|
|
238
|
+
### Do
|
|
239
|
+
|
|
240
|
+
- Keep tag labels short; long ones truncate with an ellipsis.
|
|
241
|
+
- Show the picked values somewhere else too when the field is narrow and many are picked.
|
|
242
|
+
|
|
243
|
+
### Don't
|
|
244
|
+
|
|
245
|
+
- Don't use it for one value; use `bf-combobox`.
|
|
246
|
+
- Don't make it the only way to see a long list of picks; tags wrap but get hard to scan.
|
|
247
|
+
|
|
248
|
+
## Migration notes
|
|
249
|
+
|
|
250
|
+
### 0.3.1
|
|
251
|
+
|
|
252
|
+
New `bf-multi-combobox` (`BfMultiComboboxComponent<T>`) in `@bitforgehq/ui/overlay`: the
|
|
253
|
+
combobox for several values, `[(value)]` a `readonly T[]`. Picked values are tags inside the
|
|
254
|
+
field, each with a remove button named by the new `comboboxRemove(label)` label ("Remove
|
|
255
|
+
Design"); `Backspace` in the empty field removes the last tag, the list stays open after a pick
|
|
256
|
+
and picked options carry a check mark (`aria-multiselectable`, `aria-selected`). Same options,
|
|
257
|
+
filtering, server search and Signal Forms contract as `bf-combobox`.
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# bf-nav-group
|
|
2
|
+
|
|
3
|
+
A titled group of sidebar links with a small heading that names it
|
|
4
|
+
|
|
5
|
+
`BfNavGroupComponent` from `@bitforgehq/ui` · Navigation · since 0.3.1
|
|
6
|
+
|
|
7
|
+
## Example
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { Component } from '@angular/core';
|
|
11
|
+
import { RouterLink, RouterLinkActive } from '@angular/router';
|
|
12
|
+
import { BfNavGroupComponent, BfNavItemComponent, BfSidebarComponent } from '@bitforgehq/ui';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The bf-nav-group overview.
|
|
16
|
+
*
|
|
17
|
+
* Live demo of `bf-nav-group`: two titled groups of `a[bfNavItem]` router links in a sidebar;
|
|
18
|
+
* each heading names its group for screen readers.
|
|
19
|
+
*/
|
|
20
|
+
@Component({
|
|
21
|
+
selector: 'app-example-nav-group-overview',
|
|
22
|
+
imports: [
|
|
23
|
+
RouterLink,
|
|
24
|
+
RouterLinkActive,
|
|
25
|
+
BfNavGroupComponent,
|
|
26
|
+
BfNavItemComponent,
|
|
27
|
+
BfSidebarComponent,
|
|
28
|
+
],
|
|
29
|
+
template: `
|
|
30
|
+
<bf-sidebar label="Component groups">
|
|
31
|
+
<bf-nav-group label="Navigation">
|
|
32
|
+
<a
|
|
33
|
+
bfNavItem
|
|
34
|
+
routerLink="/components/nav-group"
|
|
35
|
+
routerLinkActive
|
|
36
|
+
ariaCurrentWhenActive="page"
|
|
37
|
+
>Nav group</a
|
|
38
|
+
>
|
|
39
|
+
<a bfNavItem routerLink="/components/nav-item" routerLinkActive ariaCurrentWhenActive="page"
|
|
40
|
+
>Nav item</a
|
|
41
|
+
>
|
|
42
|
+
</bf-nav-group>
|
|
43
|
+
<bf-nav-group label="Data display">
|
|
44
|
+
<a bfNavItem routerLink="/components/list" routerLinkActive ariaCurrentWhenActive="page"
|
|
45
|
+
>List</a
|
|
46
|
+
>
|
|
47
|
+
<a bfNavItem routerLink="/components/card" routerLinkActive ariaCurrentWhenActive="page"
|
|
48
|
+
>Card</a
|
|
49
|
+
>
|
|
50
|
+
</bf-nav-group>
|
|
51
|
+
</bf-sidebar>
|
|
52
|
+
`,
|
|
53
|
+
})
|
|
54
|
+
export class NavGroupOverviewExampleComponent {}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Overview
|
|
58
|
+
|
|
59
|
+
A titled group of `a[bfNavItem]` links inside a `bf-sidebar`: `label` is shown as a small
|
|
60
|
+
heading above them and names the group (`role="group"`) for screen readers, also while the
|
|
61
|
+
sidebar is collapsed and the heading is hidden.
|
|
62
|
+
|
|
63
|
+
A titled group of [`a[bfNavItem]`](https://bitforge-ui.web.app/components/nav-item) links inside a [`bf-sidebar`](https://bitforge-ui.web.app/components/sidebar): its `label` is a small heading above the links and the group's accessible name.
|
|
64
|
+
|
|
65
|
+
## When to use
|
|
66
|
+
|
|
67
|
+
Use `bf-nav-group` to split a long sidebar into sections, such as Workspace and Admin. Leave a short sidebar ungrouped, with the links straight in `bf-sidebar`. In the Capacitor shell use [bf-tabs](https://bitforge-ui.web.app/components/tabs) with [bf-tab-bar](https://bitforge-ui.web.app/components/tab-bar) instead.
|
|
68
|
+
|
|
69
|
+
## Basic usage
|
|
70
|
+
|
|
71
|
+
Put the group's links in its default slot and its name in `label`. Groups stack with 24 px between them.
|
|
72
|
+
|
|
73
|
+
## In a collapsed sidebar
|
|
74
|
+
|
|
75
|
+
While the sidebar is collapsed to an icon rail the heading is hidden visually (`sr-only`), so only the icons show, and it still names the group for screen readers.
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
import { Component } from '@angular/core';
|
|
79
|
+
import { RouterLink, RouterLinkActive } from '@angular/router';
|
|
80
|
+
import { BfNavGroupComponent, BfNavItemComponent, BfSidebarComponent } from '@bitforgehq/ui';
|
|
81
|
+
import { BfIconComponent } from '@bitforgehq/ui/icons';
|
|
82
|
+
import { Layers, LayoutList, PanelTop, SquareStack } from 'lucide';
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Groups in a collapsed sidebar.
|
|
86
|
+
*
|
|
87
|
+
* The headings are hidden visually and still name their groups for screen readers; the items
|
|
88
|
+
* show their icons only.
|
|
89
|
+
*/
|
|
90
|
+
@Component({
|
|
91
|
+
selector: 'app-example-nav-group-collapsed',
|
|
92
|
+
imports: [
|
|
93
|
+
RouterLink,
|
|
94
|
+
RouterLinkActive,
|
|
95
|
+
BfIconComponent,
|
|
96
|
+
BfNavGroupComponent,
|
|
97
|
+
BfNavItemComponent,
|
|
98
|
+
BfSidebarComponent,
|
|
99
|
+
],
|
|
100
|
+
template: `
|
|
101
|
+
<bf-sidebar label="Component groups, collapsed" [collapsed]="true">
|
|
102
|
+
<bf-nav-group label="Navigation">
|
|
103
|
+
<a
|
|
104
|
+
bfNavItem
|
|
105
|
+
routerLink="/components/nav-group"
|
|
106
|
+
routerLinkActive
|
|
107
|
+
ariaCurrentWhenActive="page"
|
|
108
|
+
>
|
|
109
|
+
<bf-icon bfSlot="icon" [icon]="layers" />
|
|
110
|
+
Nav group
|
|
111
|
+
</a>
|
|
112
|
+
<a bfNavItem routerLink="/components/sidebar" routerLinkActive ariaCurrentWhenActive="page">
|
|
113
|
+
<bf-icon bfSlot="icon" [icon]="panelTop" />
|
|
114
|
+
Sidebar
|
|
115
|
+
</a>
|
|
116
|
+
</bf-nav-group>
|
|
117
|
+
<bf-nav-group label="Data display">
|
|
118
|
+
<a bfNavItem routerLink="/components/list" routerLinkActive ariaCurrentWhenActive="page">
|
|
119
|
+
<bf-icon bfSlot="icon" [icon]="list" />
|
|
120
|
+
List
|
|
121
|
+
</a>
|
|
122
|
+
<a bfNavItem routerLink="/components/card" routerLinkActive ariaCurrentWhenActive="page">
|
|
123
|
+
<bf-icon bfSlot="icon" [icon]="card" />
|
|
124
|
+
Card
|
|
125
|
+
</a>
|
|
126
|
+
</bf-nav-group>
|
|
127
|
+
</bf-sidebar>
|
|
128
|
+
`,
|
|
129
|
+
})
|
|
130
|
+
export class NavGroupCollapsedExampleComponent {
|
|
131
|
+
protected readonly layers = Layers;
|
|
132
|
+
protected readonly panelTop = PanelTop;
|
|
133
|
+
protected readonly list = LayoutList;
|
|
134
|
+
protected readonly card = SquareStack;
|
|
135
|
+
}
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## Desktop
|
|
139
|
+
|
|
140
|
+
The group itself is static; its links carry the hover, pressed and focus styles described on the [nav item](https://bitforge-ui.web.app/components/nav-item) page.
|
|
141
|
+
|
|
142
|
+
## Theming
|
|
143
|
+
|
|
144
|
+
- Heading: `text-caption` in `text-muted`, inset 12 px to line up with the item labels.
|
|
145
|
+
- Items: 4 px apart.
|
|
146
|
+
|
|
147
|
+
## Accessibility
|
|
148
|
+
|
|
149
|
+
The host has `role="group"` and `aria-labelledby` pointing at its heading, so a screen reader announces the group's name when focus enters it ("Workspace, group"). The heading is text, not a heading element, so it does not add to the page outline. Collapsed, the heading is `sr-only` and keeps naming the group.
|
|
150
|
+
|
|
151
|
+
## Inputs
|
|
152
|
+
|
|
153
|
+
| Name | Type | Default | Description |
|
|
154
|
+
| ------- | -------- | -------- | ------------------------------------------------------------- |
|
|
155
|
+
| `label` | `string` | required | The group's heading and accessible name, such as "Workspace". |
|
|
156
|
+
|
|
157
|
+
## Slots
|
|
158
|
+
|
|
159
|
+
- `default`: The group's `a[bfNavItem]` links.
|
|
160
|
+
|
|
161
|
+
## Notes
|
|
162
|
+
|
|
163
|
+
### Do
|
|
164
|
+
|
|
165
|
+
- Give each group a short noun as its label.
|
|
166
|
+
- Keep the links of one area together in a group.
|
|
167
|
+
|
|
168
|
+
### Don't
|
|
169
|
+
|
|
170
|
+
- Don't nest groups; a sidebar has one level of groups.
|
|
171
|
+
- Don't put a group in a `bf-top-nav`; a top nav is a single row.
|
|
172
|
+
|
|
173
|
+
## Migration notes
|
|
174
|
+
|
|
175
|
+
### 0.3.1
|
|
176
|
+
|
|
177
|
+
New `bf-nav-group`: a titled group of sidebar links, `role="group"` named by its small heading,
|
|
178
|
+
which stays the group's name (hidden visually) while the sidebar is collapsed.
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
# a[bfNavItem]
|
|
2
|
+
|
|
3
|
+
Navigation link for a sidebar or top nav, with icon, label and count
|
|
4
|
+
|
|
5
|
+
`BfNavItemComponent` from `@bitforgehq/ui` · Navigation · since 0.3.1
|
|
6
|
+
|
|
7
|
+
## Example
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { Component } from '@angular/core';
|
|
11
|
+
import { RouterLink, RouterLinkActive } from '@angular/router';
|
|
12
|
+
import { BfNavItemComponent, BfSidebarComponent } from '@bitforgehq/ui';
|
|
13
|
+
import { BfIconComponent } from '@bitforgehq/ui/icons';
|
|
14
|
+
import { Bell, Inbox, ListTodo } from 'lucide';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The a[bfNavItem] overview.
|
|
18
|
+
*
|
|
19
|
+
* Live demo of `a[bfNavItem]`: router links with an icon, the current page (this one) in the
|
|
20
|
+
* tonal fill, a count badge and the sm, md and lg heights; then the same links in a collapsed
|
|
21
|
+
* sidebar, icon only, the badge a dot and the label a tooltip.
|
|
22
|
+
*/
|
|
23
|
+
@Component({
|
|
24
|
+
selector: 'app-example-nav-item-overview',
|
|
25
|
+
imports: [RouterLink, RouterLinkActive, BfIconComponent, BfNavItemComponent, BfSidebarComponent],
|
|
26
|
+
template: `
|
|
27
|
+
<div class="flex flex-wrap items-start gap-4">
|
|
28
|
+
<bf-sidebar label="Nav items">
|
|
29
|
+
<a
|
|
30
|
+
bfNavItem
|
|
31
|
+
routerLink="/components/nav-item"
|
|
32
|
+
routerLinkActive
|
|
33
|
+
ariaCurrentWhenActive="page"
|
|
34
|
+
>
|
|
35
|
+
<bf-icon bfSlot="icon" [icon]="listTodo" />
|
|
36
|
+
Nav item
|
|
37
|
+
</a>
|
|
38
|
+
<a
|
|
39
|
+
bfNavItem
|
|
40
|
+
routerLink="/components/badge"
|
|
41
|
+
routerLinkActive
|
|
42
|
+
ariaCurrentWhenActive="page"
|
|
43
|
+
[badge]="12"
|
|
44
|
+
>
|
|
45
|
+
<bf-icon bfSlot="icon" [icon]="inbox" />
|
|
46
|
+
Badge
|
|
47
|
+
</a>
|
|
48
|
+
<a bfNavItem size="sm" routerLink="/density" routerLinkActive ariaCurrentWhenActive="page">
|
|
49
|
+
<bf-icon bfSlot="icon" [icon]="bell" />
|
|
50
|
+
Small · 32
|
|
51
|
+
</a>
|
|
52
|
+
<a bfNavItem size="lg" routerLink="/density" routerLinkActive ariaCurrentWhenActive="page">
|
|
53
|
+
<bf-icon bfSlot="icon" [icon]="bell" />
|
|
54
|
+
Large · 48
|
|
55
|
+
</a>
|
|
56
|
+
</bf-sidebar>
|
|
57
|
+
<bf-sidebar label="Nav items, collapsed" [collapsed]="true">
|
|
58
|
+
<a
|
|
59
|
+
bfNavItem
|
|
60
|
+
routerLink="/components/nav-item"
|
|
61
|
+
routerLinkActive
|
|
62
|
+
ariaCurrentWhenActive="page"
|
|
63
|
+
>
|
|
64
|
+
<bf-icon bfSlot="icon" [icon]="listTodo" />
|
|
65
|
+
Nav item
|
|
66
|
+
</a>
|
|
67
|
+
<a
|
|
68
|
+
bfNavItem
|
|
69
|
+
routerLink="/components/badge"
|
|
70
|
+
routerLinkActive
|
|
71
|
+
ariaCurrentWhenActive="page"
|
|
72
|
+
[badge]="12"
|
|
73
|
+
>
|
|
74
|
+
<bf-icon bfSlot="icon" [icon]="inbox" />
|
|
75
|
+
Badge
|
|
76
|
+
</a>
|
|
77
|
+
</bf-sidebar>
|
|
78
|
+
</div>
|
|
79
|
+
`,
|
|
80
|
+
})
|
|
81
|
+
export class NavItemOverviewExampleComponent {
|
|
82
|
+
protected readonly listTodo = ListTodo;
|
|
83
|
+
protected readonly inbox = Inbox;
|
|
84
|
+
protected readonly bell = Bell;
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Overview
|
|
89
|
+
|
|
90
|
+
One destination of a `bf-sidebar` or `bf-top-nav`: put `bfNavItem` on your own `<a>` with
|
|
91
|
+
`routerLink`, `routerLinkActive` and `ariaCurrentWhenActive="page"`, and the item shows the
|
|
92
|
+
current page from `aria-current`. An icon goes in the `icon` slot, the label in the default
|
|
93
|
+
slot; `badge` adds a count. In a collapsed sidebar only the icon shows, the label stays for
|
|
94
|
+
screen readers and becomes the `title` tooltip.
|
|
95
|
+
|
|
96
|
+
One destination of a [`bf-sidebar`](https://bitforge-ui.web.app/components/sidebar) or a [`bf-top-nav`](https://bitforge-ui.web.app/components/top-nav): put `bfNavItem` on your own `<a>` with `routerLink`, `routerLinkActive` and `ariaCurrentWhenActive="page"`. An icon goes in `bfSlot="icon"`, the label in the default slot, and `badge` adds a count. The item reads its layout from its container: a full-width row in a sidebar, an inline item in a top nav, an icon in a collapsed sidebar.
|
|
97
|
+
|
|
98
|
+
## When to use
|
|
99
|
+
|
|
100
|
+
Use `a[bfNavItem]` for each link of a `bf-sidebar` or `bf-top-nav`. Use [`a[bfButton]`](https://bitforge-ui.web.app/components/button) with `variant="ghost"` instead for a link that is not part of the app's navigation, and [bf-list-item](https://bitforge-ui.web.app/components/list-item) with a `bfAction` link for a row of a list. In the Capacitor shell use [bf-tabs](https://bitforge-ui.web.app/components/tabs) with [bf-tab-bar](https://bitforge-ui.web.app/components/tab-bar) instead.
|
|
101
|
+
|
|
102
|
+
## Basic usage
|
|
103
|
+
|
|
104
|
+
The router sets `aria-current="page"` on the current page's link (`routerLinkActive` with `ariaCurrentWhenActive="page"`), and the item shows it in the tonal fill (`primary-soft`, the same as an `a[bfButton]` ghost link); the widget never imports the router. Other items are neutral (`text-muted`) until hovered. The label truncates with an ellipsis in a narrow sidebar.
|
|
105
|
+
|
|
106
|
+
## Count badge
|
|
107
|
+
|
|
108
|
+
`badge` takes a number and shows it as a pill at the inline end; `null` (the default) shows none. The pill is hidden from screen readers and the count is announced after the label with `provideBfLabels()`'s `navBadge(count)` ("3 new"), so the link's name is "Inbox 3 new".
|
|
109
|
+
|
|
110
|
+
## Collapsed
|
|
111
|
+
|
|
112
|
+
In a collapsed `bf-sidebar` the item shows only its icon in a square of its height step. The label stays in the DOM as screen-reader text, so the link's name does not change, and becomes the link's `title` (the tooltip on hover, kept up to date when the label changes); your own `title` comes back when the sidebar expands. The badge becomes a small dot on the icon's corner and is still announced. Give every item an icon, or it shows nothing while collapsed. For a styled tooltip that also shows on keyboard focus, add `bfTooltip` from `@bitforgehq/ui/overlay` with the label's text while collapsed (an empty text while expanded shows none) and `bfTooltipPlacement="end"` (see [bf-tooltip](https://bitforge-ui.web.app/components/tooltip)).
|
|
113
|
+
|
|
114
|
+
## Sizes
|
|
115
|
+
|
|
116
|
+
`size` sets the height: `sm` 32, `md` 40, `lg` 48 px. Unset, it follows `provideBfDefaults()` (`controlSize`, `md` by default). The height is a minimum, so a label at a large text size grows the item instead of clipping it.
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
import { Component } from '@angular/core';
|
|
120
|
+
import { BfNavItemComponent, BfSidebarComponent } from '@bitforgehq/ui';
|
|
121
|
+
import { BfIconComponent } from '@bitforgehq/ui/icons';
|
|
122
|
+
import { Bell } from 'lucide';
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* The three heights of a nav item.
|
|
126
|
+
*
|
|
127
|
+
* `size` sets 32, 40 or 48 px; unset, the item follows `provideBfDefaults()`'s `controlSize`.
|
|
128
|
+
*/
|
|
129
|
+
@Component({
|
|
130
|
+
selector: 'app-example-nav-item-sizes',
|
|
131
|
+
imports: [BfIconComponent, BfNavItemComponent, BfSidebarComponent],
|
|
132
|
+
template: `
|
|
133
|
+
<bf-sidebar label="Nav item sizes">
|
|
134
|
+
<a bfNavItem size="sm" href="#sizes">
|
|
135
|
+
<bf-icon bfSlot="icon" [icon]="bell" />
|
|
136
|
+
Small · 32
|
|
137
|
+
</a>
|
|
138
|
+
<a bfNavItem size="md" href="#sizes">
|
|
139
|
+
<bf-icon bfSlot="icon" [icon]="bell" />
|
|
140
|
+
Medium · 40
|
|
141
|
+
</a>
|
|
142
|
+
<a bfNavItem size="lg" href="#sizes">
|
|
143
|
+
<bf-icon bfSlot="icon" [icon]="bell" />
|
|
144
|
+
Large · 48
|
|
145
|
+
</a>
|
|
146
|
+
</bf-sidebar>
|
|
147
|
+
`,
|
|
148
|
+
})
|
|
149
|
+
export class NavItemSizesExampleComponent {
|
|
150
|
+
protected readonly bell = Bell;
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Desktop
|
|
155
|
+
|
|
156
|
+
Hover gives a `surface-muted` fill with the `text` colour and the pointer; pressing gives `surface-pressed`. The current page keeps its tonal fill, one step darker on hover and again while pressed. The focus ring shows on keyboard focus only (`focus-visible`). Under a fine pointer the item's box is its hit area; under a coarse pointer (a touchscreen laptop) it grows to at least 44 px tall (`pointer-coarse:min-h-hit`) instead of extending an invisible hit area over its neighbours, because it holds your label. A link keeps the browser's behaviour: Ctrl/Cmd-click and a middle click open it in a new tab.
|
|
157
|
+
|
|
158
|
+
## Theming
|
|
159
|
+
|
|
160
|
+
- Rest: transparent with `text-muted`; hover `surface-muted` with `text`; pressed `surface-pressed`.
|
|
161
|
+
- Current page (`aria-current="page"`): `primary-soft` with `primary-strong`, hover `primary-soft-hover`, pressed `primary-soft-pressed`; under forced colours a `LinkText` outline.
|
|
162
|
+
- Shape: `rounded-control`; label `font-bold`, `text-label-xs` / `text-label-sm` / `text-label` by size.
|
|
163
|
+
- Badge: `primary` with `on-primary`, `text-micro`; collapsed, a `primary` dot (`CanvasText` under forced colours).
|
|
164
|
+
- Focus: `ring` (4 px).
|
|
165
|
+
|
|
166
|
+
## Accessibility
|
|
167
|
+
|
|
168
|
+
The item is the consumer's native `<a>`: exposed as a link, reached with Tab and followed with Enter, its name being its label (plus the badge announcement). The current page carries `aria-current="page"`, which screen readers announce; the fill is not the only cue, as an outline marks it under forced colours. The icon slot is decorative (a `bf-icon` is `aria-hidden` by default). Collapsed, the label is visually hidden but still the name, and `title` adds a pointer tooltip only; the badge dot is `aria-hidden`, its count announced through the screen-reader text. In Safari, Tab reaches links only when the user turns on "Press Tab to highlight each item" (Option+Tab otherwise).
|
|
169
|
+
|
|
170
|
+
### Keyboard
|
|
171
|
+
|
|
172
|
+
| Key | Action |
|
|
173
|
+
| ---------------- | ------------------------------------------------- |
|
|
174
|
+
| <kbd>Tab</kbd> | Moves focus to the link |
|
|
175
|
+
| <kbd>Enter</kbd> | Follows the link; a link ignores <kbd>Space</kbd> |
|
|
176
|
+
|
|
177
|
+
## Inputs
|
|
178
|
+
|
|
179
|
+
| Name | Type | Default | Description |
|
|
180
|
+
| ------- | ----------------------------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
181
|
+
| `size` | `BfSize \| undefined` (`'sm' \| 'md' \| 'lg' \| undefined`) | `undefined` | Height step: sm 32, md 40, lg 48 px (44 px at least under a coarse pointer); unset follows `provideBfDefaults()` (`controlSize`, `md` by default). |
|
|
182
|
+
| `badge` | `number \| null` | `null` | A count shown as a pill at the inline end (a dot while collapsed), announced with `provideBfLabels()`'s `navBadge(count)` ("3 new"); `null` shows none. |
|
|
183
|
+
|
|
184
|
+
## Slots
|
|
185
|
+
|
|
186
|
+
- `icon`: An icon before the label, a `bf-icon`; the only thing shown while collapsed.
|
|
187
|
+
- `default`: The label.
|
|
188
|
+
|
|
189
|
+
## Notes
|
|
190
|
+
|
|
191
|
+
### Do
|
|
192
|
+
|
|
193
|
+
- Put `routerLinkActive` and `ariaCurrentWhenActive="page"` on every item, so the current page shows.
|
|
194
|
+
- Give every item in a collapsible sidebar an icon.
|
|
195
|
+
|
|
196
|
+
### Don't
|
|
197
|
+
|
|
198
|
+
- Don't put `bfNavItem` on a `<button>`; a destination is a link, and an action belongs in the header or footer as a button.
|
|
199
|
+
- Don't set `aria-current` yourself on more than one item.
|
|
200
|
+
|
|
201
|
+
## Migration notes
|
|
202
|
+
|
|
203
|
+
### 0.3.1
|
|
204
|
+
|
|
205
|
+
New `a[bfNavItem]`: one destination of a `bf-sidebar` or `bf-top-nav` on the app's own
|
|
206
|
+
`<a routerLink routerLinkActive ariaCurrentWhenActive="page">`. Neutral at rest, the tonal fill
|
|
207
|
+
for the current page from `aria-current="page"` (as an `a[bfButton]` ghost link), an `icon` slot,
|
|
208
|
+
`size` from `controlSize` (44 px at least under a coarse pointer) and a `badge` count announced
|
|
209
|
+
with the new `navBadge(count)` label ("3 new"). In a collapsed sidebar it shows its icon only,
|
|
210
|
+
keeps its label as screen-reader text and as its `title`, and turns the badge into a dot.
|