@bitforgehq/ui 0.3.0 → 0.3.2

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.
Files changed (158) hide show
  1. package/CHANGELOG.md +598 -0
  2. package/README.md +18 -11
  3. package/ai/MANIFEST.index.json +306 -0
  4. package/ai/MANIFEST.json +8915 -3788
  5. package/ai/REFERENCE.index.json +476 -0
  6. package/ai/REFERENCE.json +5810 -2905
  7. package/ai/UI_GUIDE.md +212 -58
  8. package/ai/components/app-frame.md +328 -0
  9. package/ai/components/back-button.md +42 -2
  10. package/ai/components/badge.md +17 -7
  11. package/ai/components/bar-chart.md +188 -0
  12. package/ai/components/breadcrumb-item.md +121 -0
  13. package/ai/components/breadcrumb.md +277 -0
  14. package/ai/components/button.md +82 -15
  15. package/ai/components/calendar.md +300 -0
  16. package/ai/components/card.md +119 -4
  17. package/ai/components/chart-card.md +283 -0
  18. package/ai/components/combobox.md +411 -0
  19. package/ai/components/date-input.md +396 -0
  20. package/ai/components/datetime-input.md +215 -0
  21. package/ai/components/description-item.md +111 -0
  22. package/ai/components/description-list.md +160 -0
  23. package/ai/components/donut-chart.md +169 -0
  24. package/ai/components/empty-state.md +99 -6
  25. package/ai/components/header.md +71 -2
  26. package/ai/components/icon-button.md +74 -16
  27. package/ai/components/icon.md +63 -7
  28. package/ai/components/line-chart.md +234 -0
  29. package/ai/components/list.md +70 -6
  30. package/ai/components/menu.md +5 -0
  31. package/ai/components/multi-combobox.md +257 -0
  32. package/ai/components/nav-group.md +178 -0
  33. package/ai/components/nav-item.md +210 -0
  34. package/ai/components/page-header.md +237 -0
  35. package/ai/components/pagination.md +173 -0
  36. package/ai/components/popover.md +250 -0
  37. package/ai/components/side-panel.md +391 -0
  38. package/ai/components/sidebar-toggle.md +287 -0
  39. package/ai/components/sidebar.md +541 -0
  40. package/ai/components/skeleton.md +215 -0
  41. package/ai/components/sparkline.md +188 -0
  42. package/ai/components/stat.md +297 -0
  43. package/ai/components/tab-group.md +226 -0
  44. package/ai/components/tab-panel.md +163 -0
  45. package/ai/components/table.md +697 -0
  46. package/ai/components/tabs.md +46 -2
  47. package/ai/components/theme-menu-button.md +182 -0
  48. package/ai/components/theme-switch.md +170 -0
  49. package/ai/components/time-input.md +240 -0
  50. package/ai/components/toolbar.md +354 -0
  51. package/ai/components/tooltip.md +230 -0
  52. package/ai/components/top-nav.md +159 -0
  53. package/ai/reference/bf-app-frame-breakpoint.md +27 -0
  54. package/ai/reference/bf-app-frame-content-width.md +29 -0
  55. package/ai/reference/bf-badge-appearance.md +36 -0
  56. package/ai/reference/bf-badge-variant.md +1 -1
  57. package/ai/reference/bf-bar-chart-orientation.md +27 -0
  58. package/ai/reference/bf-button-appearance.md +36 -0
  59. package/ai/reference/bf-button-variant.md +1 -1
  60. package/ai/reference/bf-calendar-disabled-date.md +23 -0
  61. package/ai/reference/bf-calendar-harness.md +51 -0
  62. package/ai/reference/bf-calendar-weekday.md +31 -0
  63. package/ai/reference/bf-chart-card-heading-level.md +26 -0
  64. package/ai/reference/bf-chart-curve.md +28 -0
  65. package/ai/reference/bf-chart-harness.md +51 -0
  66. package/ai/reference/bf-chart-legend-position.md +30 -0
  67. package/ai/reference/bf-chart-series.md +23 -0
  68. package/ai/reference/bf-chart-value-format.md +23 -0
  69. package/ai/reference/bf-color-scheme-service.md +30 -0
  70. package/ai/reference/bf-color-scheme.md +31 -0
  71. package/ai/reference/bf-combobox-filter.md +27 -0
  72. package/ai/reference/bf-combobox-harness.md +89 -0
  73. package/ai/reference/bf-combobox-option.md +33 -0
  74. package/ai/reference/bf-compare-with.md +2 -0
  75. package/ai/reference/bf-date-input-harness.md +67 -0
  76. package/ai/reference/bf-date-input-picker.md +27 -0
  77. package/ai/reference/bf-date-time-input-harness.md +76 -0
  78. package/ai/reference/bf-date-time-input-picker.md +27 -0
  79. package/ai/reference/bf-description-list-layout.md +26 -0
  80. package/ai/reference/bf-donut-slice.md +22 -0
  81. package/ai/reference/bf-empty-state-heading-level.md +1 -1
  82. package/ai/reference/bf-empty-state-heading.md +33 -0
  83. package/ai/reference/bf-labels.md +73 -20
  84. package/ai/reference/bf-menu-button-variant.md +1 -0
  85. package/ai/reference/bf-menu-placement.md +1 -0
  86. package/ai/reference/bf-overlay-options.md +28 -27
  87. package/ai/reference/bf-overlay-placement.md +34 -0
  88. package/ai/reference/bf-overlay-position.md +1 -1
  89. package/ai/reference/bf-overlay-service.md +5 -3
  90. package/ai/reference/bf-page-header-heading-level.md +26 -0
  91. package/ai/reference/bf-pagination-harness.md +61 -0
  92. package/ai/reference/bf-platform-service.md +17 -15
  93. package/ai/reference/bf-popover-harness.md +42 -0
  94. package/ai/reference/bf-popover-placement.md +28 -0
  95. package/ai/reference/bf-popover-trigger-directive.md +56 -0
  96. package/ai/reference/bf-side-panel-config.md +35 -0
  97. package/ai/reference/bf-side-panel-harness.md +34 -0
  98. package/ai/reference/bf-side-panel-ref.md +44 -0
  99. package/ai/reference/bf-side-panel-service.md +43 -0
  100. package/ai/reference/bf-side-panel-template-context.md +32 -0
  101. package/ai/reference/bf-side-panel-width.md +28 -0
  102. package/ai/reference/bf-sidebar-state-service.md +21 -0
  103. package/ai/reference/bf-sidebar-toggle-harness.md +32 -0
  104. package/ai/reference/bf-size.md +13 -0
  105. package/ai/reference/bf-skeleton-shape.md +28 -0
  106. package/ai/reference/bf-stat-appearance.md +33 -0
  107. package/ai/reference/bf-stat-trend-intent.md +27 -0
  108. package/ai/reference/bf-stat-trend.md +27 -0
  109. package/ai/reference/bf-stat-variant.md +26 -0
  110. package/ai/reference/bf-tab-group-harness.md +44 -0
  111. package/ai/reference/bf-table-align.md +26 -0
  112. package/ai/reference/bf-table-cell-context.md +24 -0
  113. package/ai/reference/bf-table-cell-directive.md +22 -0
  114. package/ai/reference/bf-table-column.md +28 -0
  115. package/ai/reference/bf-table-harness.md +77 -0
  116. package/ai/reference/bf-table-row-key.md +21 -0
  117. package/ai/reference/bf-table-sort-direction.md +26 -0
  118. package/ai/reference/bf-table-sort.md +27 -0
  119. package/ai/reference/bf-table-stack.md +29 -0
  120. package/ai/reference/bf-theme.md +5 -4
  121. package/ai/reference/bf-time-input-harness.md +67 -0
  122. package/ai/reference/bf-time-input-hour-cycle.md +27 -0
  123. package/ai/reference/bf-time-input-picker.md +27 -0
  124. package/ai/reference/bf-tooltip-directive.md +43 -0
  125. package/ai/reference/bf-tooltip-harness.md +43 -0
  126. package/ai/reference/bf-tooltip-placement.md +28 -0
  127. package/ai/reference/bf_color_scheme_storage.md +20 -0
  128. package/ai/reference/bf_side_panel_data.md +17 -0
  129. package/ai/reference/bf_sidebar_state_storage.md +20 -0
  130. package/ai/reference/bf_theme_storage.md +3 -1
  131. package/ai/reference/provide-bf-color-scheme.md +20 -0
  132. package/ai/reference/provide-bf-labels.md +13 -4
  133. package/ai/reference/provide-bf-sidebar-state.md +20 -0
  134. package/ai/reference/slide-in-from-end.md +24 -0
  135. package/ai/reference/slide-out-to-end.md +24 -0
  136. package/bin/bf-ui.js +433 -3
  137. package/fesm2022/bitforgehq-ui-charts.mjs +3045 -0
  138. package/fesm2022/bitforgehq-ui-charts.mjs.map +1 -0
  139. package/fesm2022/bitforgehq-ui-icons.mjs +107 -3
  140. package/fesm2022/bitforgehq-ui-icons.mjs.map +1 -1
  141. package/fesm2022/bitforgehq-ui-mobile.mjs +14 -59
  142. package/fesm2022/bitforgehq-ui-mobile.mjs.map +1 -1
  143. package/fesm2022/bitforgehq-ui-overlay.mjs +6566 -38
  144. package/fesm2022/bitforgehq-ui-overlay.mjs.map +1 -1
  145. package/fesm2022/bitforgehq-ui-testing.mjs +634 -1
  146. package/fesm2022/bitforgehq-ui-testing.mjs.map +1 -1
  147. package/fesm2022/bitforgehq-ui.mjs +5708 -137
  148. package/fesm2022/bitforgehq-ui.mjs.map +1 -1
  149. package/llms-full.txt +26766 -14470
  150. package/llms.txt +106 -1
  151. package/package.json +7 -3
  152. package/theme.css +101 -0
  153. package/types/bitforgehq-ui-charts.d.ts +967 -0
  154. package/types/bitforgehq-ui-icons.d.ts +13 -1
  155. package/types/bitforgehq-ui-mobile.d.ts +25 -18
  156. package/types/bitforgehq-ui-overlay.d.ts +1895 -7
  157. package/types/bitforgehq-ui-testing.d.ts +782 -1
  158. package/types/bitforgehq-ui.d.ts +2393 -45
@@ -69,7 +69,7 @@ Lists show rows of related items: settings, conversations, people. `bf-list` is
69
69
 
70
70
  ## When to use
71
71
 
72
- Use `bf-list` for a group of rows of the same kind: settings, conversations, people. Use [bf-card](https://bitforge-ui.web.app/components/card) instead for one piece of content with its own layout, and a native `table` for data the user compares across columns.
72
+ Use `bf-list` for a group of rows of the same kind: settings, conversations, people. Use [bf-card](https://bitforge-ui.web.app/components/card) instead for one piece of content with its own layout, and [bf-table](https://bitforge-ui.web.app/components/table) for data the user compares across columns.
73
73
 
74
74
  ## Basic usage
75
75
 
@@ -128,9 +128,63 @@ import { BfCardComponent, BfListComponent, BfListItemComponent } from '@bitforge
128
128
  export class ListPlainExampleComponent {}
129
129
  ```
130
130
 
131
+ ## Divided
132
+
133
+ `divided` separates the rows with a `border` line instead of a gap and squares off their surfaces, so the hover fill of a navigation or action row reaches the list's edges. It is off by default, so existing lists keep their look. On an inset list the rows become one rounded surface (only the first and last rows keep the card radius); on a plain list they sit flat on whatever holds them.
134
+
135
+ ## On a card
136
+
137
+ To put rows on a card, use a plain divided list and pull it flush to the card's inline edges with `class="-mx-card"` (the card's `md` padding; `-mx-card-lg` on a `padding="lg"` card). The card stays the only container, the lines run edge to edge, and a row's hover fill reaches the card's edge instead of stopping at the card padding. A plain divided list gives its rows the card's inline padding, so their text stays on the card's content edge, aligned with the heading above.
138
+
139
+ ```ts
140
+ import { Component } from '@angular/core';
141
+ import {
142
+ BfActionDirective,
143
+ BfBadgeComponent,
144
+ BfCardComponent,
145
+ BfListComponent,
146
+ BfListItemComponent,
147
+ } from '@bitforgehq/ui';
148
+
149
+ /**
150
+ * A divided list flush to a card's edges.
151
+ *
152
+ * `[inset]="false" divided` with `class="-mx-card"`: the lines and the rows' hover fill run to
153
+ * the card's edges, and the row text stays aligned with the heading.
154
+ */
155
+ @Component({
156
+ selector: 'app-example-list-card',
157
+ imports: [
158
+ BfActionDirective,
159
+ BfBadgeComponent,
160
+ BfCardComponent,
161
+ BfListComponent,
162
+ BfListItemComponent,
163
+ ],
164
+ template: `
165
+ <bf-card class="max-w-md">
166
+ <h3 class="text-heading">Projects</h3>
167
+ <bf-list [inset]="false" divided class="-mx-card">
168
+ <bf-list-item subtitle="Due Friday" chevron>
169
+ <a bfSlot="title" bfAction href="#atlas">Atlas rebrand</a>
170
+ </bf-list-item>
171
+ <bf-list-item subtitle="Planning" chevron>
172
+ <a bfSlot="title" bfAction href="#harbor">Harbor onboarding</a>
173
+ </bf-list-item>
174
+ <bf-list-item subtitle="In review" chevron>
175
+ <a bfSlot="title" bfAction href="#lumen">Lumen reports</a>
176
+ <bf-badge bfSlot="trailing" variant="warning">2 open</bf-badge>
177
+ </bf-list-item>
178
+ </bf-list>
179
+ </bf-card>
180
+ `,
181
+ })
182
+ export class ListCardExampleComponent {}
183
+ ```
184
+
131
185
  ## Theming
132
186
 
133
- `inset` (default) uses the `bg` background on a `card` radius; `[inset]="false"` is transparent with no radius. The rows' own tokens are on the [`bf-list-item` page](https://bitforge-ui.web.app/components/list-item#theming).
187
+ `inset` (default) uses the `bg` background on a `card` radius; `[inset]="false"` is transparent with no radius. `divided` draws the lines in `border`; a divided inset list is a `surface` with the `card` radius, and a divided plain list pads its rows with `--spacing-card` (`px-card`). The rows' own tokens are on the [`bf-list-item` page](https://bitforge-ui.web.app/components/list-item#theming).
134
188
 
135
189
  ## Accessibility
136
190
 
@@ -138,9 +192,10 @@ export class ListPlainExampleComponent {}
138
192
 
139
193
  ## Inputs
140
194
 
141
- | Name | Type | Default | Description |
142
- | ------- | --------- | ------- | -------------------------------------------------------------------------------- |
143
- | `inset` | `boolean` | `true` | Grouped look with the warm container; false renders rows on the page background. |
195
+ | Name | Type | Default | Description |
196
+ | --------- | --------- | ------- | -------------------------------------------------------------------------------------------------------------------------------- |
197
+ | `inset` | `boolean` | `true` | Grouped look with the warm container; false renders rows on the page background. |
198
+ | `divided` | `boolean` | `false` | Separates the rows with a `border` line instead of a gap, with square rows whose hover reaches the list's edges. Off by default. |
144
199
 
145
200
  ## Slots
146
201
 
@@ -151,15 +206,24 @@ export class ListPlainExampleComponent {}
151
206
  ### Do
152
207
 
153
208
  - Group rows that belong together in one `bf-list`; start a new list under a heading for a new group.
154
- - Use `[inset]="false"` on a card, so the card is the only container.
209
+ - Use `[inset]="false"` on a card, so the card is the only container; add `divided` and `class="-mx-card"` for rows that run edge to edge.
155
210
 
156
211
  ### Don't
157
212
 
158
213
  - Don't put anything but `bf-list-item` rows in a `bf-list`; its `role="list"` expects list items.
159
214
  - Don't nest a `bf-list` inside a row.
215
+ - Don't add your own borders or padding to the rows to separate them; use `divided`.
160
216
 
161
217
  ## Migration notes
162
218
 
219
+ ### 0.3.1
220
+
221
+ New opt-in `divided` input: a `border` line between rows instead of a gap, with square rows whose
222
+ hover fill reaches the list's edges. On a card, `<bf-list [inset]="false" divided class="-mx-card">`
223
+ runs the rows edge to edge, and the rows take the card's inline padding so their text stays aligned
224
+ with the card's content. A divided inset list is one rounded `surface`. Lists without `divided`
225
+ look as before. When to use links `bf-table` for data compared across columns.
226
+
163
227
  ### 0.3.0
164
228
 
165
229
  JSDoc of `list.component.ts` for the API reference: `@slot` docs for `default`. No behaviour change.
@@ -619,6 +619,11 @@ From the [APG menu button](https://www.w3.org/WAI/ARIA/apg/patterns/menu-button/
619
619
 
620
620
  ## Migration notes
621
621
 
622
+ ### 0.3.1
623
+
624
+ The menu's positions now come from the anchored-panel positioning the popover and tooltip share.
625
+ No behaviour change.
626
+
622
627
  ### 0.3.0
623
628
 
624
629
  New `changed` output on `bfMenuItemCheckbox`: the new `checked` state right after the user toggles
@@ -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.