@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.
- package/CHANGELOG.md +598 -0
- package/README.md +18 -11
- package/ai/MANIFEST.index.json +306 -0
- package/ai/MANIFEST.json +8915 -3788
- package/ai/REFERENCE.index.json +476 -0
- package/ai/REFERENCE.json +5810 -2905
- package/ai/UI_GUIDE.md +212 -58
- package/ai/components/app-frame.md +328 -0
- 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 +119 -4
- 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/empty-state.md +99 -6
- package/ai/components/header.md +71 -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-toggle.md +287 -0
- package/ai/components/sidebar.md +541 -0
- package/ai/components/skeleton.md +215 -0
- package/ai/components/sparkline.md +188 -0
- package/ai/components/stat.md +297 -0
- package/ai/components/tab-group.md +226 -0
- package/ai/components/tab-panel.md +163 -0
- package/ai/components/table.md +697 -0
- package/ai/components/tabs.md +46 -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 +354 -0
- package/ai/components/tooltip.md +230 -0
- package/ai/components/top-nav.md +159 -0
- package/ai/reference/bf-app-frame-breakpoint.md +27 -0
- package/ai/reference/bf-app-frame-content-width.md +29 -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-empty-state-heading-level.md +1 -1
- package/ai/reference/bf-empty-state-heading.md +33 -0
- package/ai/reference/bf-labels.md +73 -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-sidebar-state-service.md +21 -0
- package/ai/reference/bf-sidebar-toggle-harness.md +32 -0
- package/ai/reference/bf-size.md +13 -0
- package/ai/reference/bf-skeleton-shape.md +28 -0
- package/ai/reference/bf-stat-appearance.md +33 -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-table-stack.md +29 -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_sidebar_state_storage.md +20 -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 +13 -4
- package/ai/reference/provide-bf-sidebar-state.md +20 -0
- package/ai/reference/slide-in-from-end.md +24 -0
- package/ai/reference/slide-out-to-end.md +24 -0
- package/bin/bf-ui.js +433 -3
- 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 +6566 -38
- package/fesm2022/bitforgehq-ui-overlay.mjs.map +1 -1
- package/fesm2022/bitforgehq-ui-testing.mjs +634 -1
- package/fesm2022/bitforgehq-ui-testing.mjs.map +1 -1
- package/fesm2022/bitforgehq-ui.mjs +5708 -137
- package/fesm2022/bitforgehq-ui.mjs.map +1 -1
- package/llms-full.txt +26766 -14470
- package/llms.txt +106 -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 +1895 -7
- package/types/bitforgehq-ui-testing.d.ts +782 -1
- package/types/bitforgehq-ui.d.ts +2393 -45
|
@@ -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.
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
# bf-page-header
|
|
2
|
+
|
|
3
|
+
Desktop page header with breadcrumb, title, subtitle, badges and a row of actions
|
|
4
|
+
|
|
5
|
+
`BfPageHeaderComponent` from `@bitforgehq/ui` · Navigation · since 0.3.1
|
|
6
|
+
|
|
7
|
+
## Example
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { Component, signal } from '@angular/core';
|
|
11
|
+
import { RouterLink } from '@angular/router';
|
|
12
|
+
import {
|
|
13
|
+
BfBadgeComponent,
|
|
14
|
+
BfBreadcrumbComponent,
|
|
15
|
+
BfBreadcrumbItemComponent,
|
|
16
|
+
BfButtonComponent,
|
|
17
|
+
BfPageHeaderComponent,
|
|
18
|
+
} from '@bitforgehq/ui';
|
|
19
|
+
import { BfIconComponent } from '@bitforgehq/ui/icons';
|
|
20
|
+
import {
|
|
21
|
+
BfMenuButtonComponent,
|
|
22
|
+
BfMenuComponent,
|
|
23
|
+
BfMenuItemDirective,
|
|
24
|
+
} from '@bitforgehq/ui/overlay';
|
|
25
|
+
import { Ellipsis, Plus } from 'lucide';
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The bf-page-header overview.
|
|
29
|
+
*
|
|
30
|
+
* Live demo of `bf-page-header`: a breadcrumb above the page title, a subtitle, two badges, and
|
|
31
|
+
* the actions (a ghost Share, a primary New task and an icon-only "More actions" menu button)
|
|
32
|
+
* that report to the result line and wrap under the title in a narrow window.
|
|
33
|
+
*/
|
|
34
|
+
@Component({
|
|
35
|
+
selector: 'app-example-page-header-overview',
|
|
36
|
+
imports: [
|
|
37
|
+
RouterLink,
|
|
38
|
+
BfBadgeComponent,
|
|
39
|
+
BfBreadcrumbComponent,
|
|
40
|
+
BfBreadcrumbItemComponent,
|
|
41
|
+
BfButtonComponent,
|
|
42
|
+
BfIconComponent,
|
|
43
|
+
BfMenuButtonComponent,
|
|
44
|
+
BfMenuComponent,
|
|
45
|
+
BfMenuItemDirective,
|
|
46
|
+
BfPageHeaderComponent,
|
|
47
|
+
],
|
|
48
|
+
template: `
|
|
49
|
+
<div class="flex flex-col gap-3">
|
|
50
|
+
<bf-page-header
|
|
51
|
+
title="Atlas rebrand"
|
|
52
|
+
subtitle="12 of 18 tasks done · due Friday"
|
|
53
|
+
[headingLevel]="2"
|
|
54
|
+
>
|
|
55
|
+
<bf-breadcrumb bfSlot="breadcrumb" label="Page header breadcrumb">
|
|
56
|
+
@for (crumb of trail; track crumb.path) {
|
|
57
|
+
<li bfBreadcrumbItem>
|
|
58
|
+
<a [routerLink]="crumb.path">{{ crumb.label }}</a>
|
|
59
|
+
</li>
|
|
60
|
+
}
|
|
61
|
+
<li bfBreadcrumbItem>Atlas rebrand</li>
|
|
62
|
+
</bf-breadcrumb>
|
|
63
|
+
<bf-badge bfSlot="meta" variant="success">On track</bf-badge>
|
|
64
|
+
<bf-badge bfSlot="meta" variant="info">Design</bf-badge>
|
|
65
|
+
<bf-button bfSlot="actions" variant="ghost" (clicked)="status.set('Shared.')"
|
|
66
|
+
>Share</bf-button
|
|
67
|
+
>
|
|
68
|
+
<bf-button bfSlot="actions" (clicked)="status.set('New task.')">
|
|
69
|
+
<bf-icon bfSlot="icon-start" [icon]="plus" />
|
|
70
|
+
New task
|
|
71
|
+
</bf-button>
|
|
72
|
+
<bf-menu-button
|
|
73
|
+
bfSlot="actions"
|
|
74
|
+
[menu]="more"
|
|
75
|
+
iconOnly
|
|
76
|
+
label="More actions"
|
|
77
|
+
variant="text"
|
|
78
|
+
placement="below-end"
|
|
79
|
+
>
|
|
80
|
+
<bf-icon [icon]="ellipsis" />
|
|
81
|
+
</bf-menu-button>
|
|
82
|
+
</bf-page-header>
|
|
83
|
+
<p data-status aria-live="polite" class="text-caption text-text-muted">{{ status() }}</p>
|
|
84
|
+
</div>
|
|
85
|
+
|
|
86
|
+
<ng-template #more>
|
|
87
|
+
<bf-menu>
|
|
88
|
+
<button bfMenuItem (triggered)="status.set('Renamed.')">Rename</button>
|
|
89
|
+
<button bfMenuItem (triggered)="status.set('Archived.')">Archive</button>
|
|
90
|
+
</bf-menu>
|
|
91
|
+
</ng-template>
|
|
92
|
+
`,
|
|
93
|
+
})
|
|
94
|
+
export class PageHeaderOverviewExampleComponent {
|
|
95
|
+
protected readonly plus = Plus;
|
|
96
|
+
protected readonly ellipsis = Ellipsis;
|
|
97
|
+
protected readonly status = signal('Pick an action.');
|
|
98
|
+
protected readonly trail: readonly { path: string; label: string }[] = [
|
|
99
|
+
{ path: '/', label: 'Home' },
|
|
100
|
+
{ path: '/components', label: 'Projects' },
|
|
101
|
+
];
|
|
102
|
+
}
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Overview
|
|
106
|
+
|
|
107
|
+
The top of a desktop page: a breadcrumb above, then the page title with its subtitle and
|
|
108
|
+
badges, and the page's actions at the inline end, wrapping under the title in a narrow window.
|
|
109
|
+
|
|
110
|
+
The top of a desktop page: a breadcrumb above, the page title with an optional subtitle and badges, and the page's actions at the inline end. `bf-page-header` lays out what you project; it draws nothing of its own.
|
|
111
|
+
|
|
112
|
+
`bf-page-header` and the shell's [bf-header](https://bitforge-ui.web.app/components/header) are two siblings, not one shell-aware widget. A shell-aware mode would need the root entry to read the shell's platform and scroll state (a service and an import of `@bitforgehq/ui/mobile`, both forbidden in a root widget), and the large-title collapse and the scroll-under surface depend on `bf-content`, which a desktop page does not have. The two share the slot names that mean the same thing.
|
|
113
|
+
|
|
114
|
+
## When to use
|
|
115
|
+
|
|
116
|
+
Use `bf-page-header` once at the top of every page of a desktop web app, and with `headingLevel` 2 for a section further down a long page. In the Capacitor shell use [bf-header](https://bitforge-ui.web.app/components/header) instead (large title, scroll-under surface).
|
|
117
|
+
|
|
118
|
+
## Basic usage
|
|
119
|
+
|
|
120
|
+
Set `title` and `subtitle`, put the trail in the `breadcrumb` slot, badges in `meta` and buttons in `actions`. The main action is a primary [bf-button](https://bitforge-ui.web.app/components/button), secondary ones `ghost` or `outline`, and the rest go into an icon-only [bf-menu-button](https://bitforge-ui.web.app/components/menu-button) labelled "More actions". In a narrow window the actions wrap under the title. The example at the top of this page shows all of them.
|
|
121
|
+
|
|
122
|
+
## Badges and rich titles
|
|
123
|
+
|
|
124
|
+
The `meta` slot sits beside the title in a wrapping row, so badges drop under a long title instead of squeezing it. When the title needs more than text, leave `title` empty and project the content into the `title` slot; it renders inside the heading.
|
|
125
|
+
|
|
126
|
+
```ts
|
|
127
|
+
import { Component } from '@angular/core';
|
|
128
|
+
import { BfBadgeComponent, BfPageHeaderComponent } from '@bitforgehq/ui';
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Badges beside the title.
|
|
132
|
+
*
|
|
133
|
+
* The `meta` slot holds badges (and other short facts) beside the title; they wrap under it when
|
|
134
|
+
* the title is long or the window narrow. The `title` slot takes rich title content when the
|
|
135
|
+
* plain `title` input is not enough.
|
|
136
|
+
*/
|
|
137
|
+
@Component({
|
|
138
|
+
selector: 'app-example-page-header-meta',
|
|
139
|
+
imports: [BfBadgeComponent, BfPageHeaderComponent],
|
|
140
|
+
template: `
|
|
141
|
+
<div class="flex flex-col gap-8">
|
|
142
|
+
<bf-page-header title="Quarterly brand refresh for the Atlas launch" [headingLevel]="2">
|
|
143
|
+
<bf-badge bfSlot="meta" variant="warning">At risk</bf-badge>
|
|
144
|
+
<bf-badge bfSlot="meta" variant="info">Design</bf-badge>
|
|
145
|
+
<bf-badge bfSlot="meta">Q4</bf-badge>
|
|
146
|
+
</bf-page-header>
|
|
147
|
+
<bf-page-header subtitle="Version 3, edited today" [headingLevel]="2">
|
|
148
|
+
<span bfSlot="title">Atlas rebrand <span class="text-text-muted">/ Brand book</span></span>
|
|
149
|
+
<bf-badge bfSlot="meta" variant="primary">Draft</bf-badge>
|
|
150
|
+
</bf-page-header>
|
|
151
|
+
</div>
|
|
152
|
+
`,
|
|
153
|
+
})
|
|
154
|
+
export class PageHeaderMetaExampleComponent {}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Heading level
|
|
158
|
+
|
|
159
|
+
The title is the page's `h1`. Set `headingLevel` to `2` for a header that opens a section of a page that already has one. The examples on this page use `2`, because the docs page has its own `h1`.
|
|
160
|
+
|
|
161
|
+
```ts
|
|
162
|
+
import { Component } from '@angular/core';
|
|
163
|
+
import { BfButtonComponent, BfPageHeaderComponent } from '@bitforgehq/ui';
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* A section header with an h2.
|
|
167
|
+
*
|
|
168
|
+
* The page's own header keeps the `h1`; a header for a section further down the same page sets
|
|
169
|
+
* `headingLevel` to `2`, so the page outline stays in order. (Every example on this docs page is
|
|
170
|
+
* at level 2, because the docs page has its own `h1`.)
|
|
171
|
+
*/
|
|
172
|
+
@Component({
|
|
173
|
+
selector: 'app-example-page-header-heading-level',
|
|
174
|
+
imports: [BfButtonComponent, BfPageHeaderComponent],
|
|
175
|
+
template: `
|
|
176
|
+
<div class="flex flex-col gap-8">
|
|
177
|
+
<bf-page-header title="Milestones" subtitle="3 upcoming" [headingLevel]="2">
|
|
178
|
+
<bf-button bfSlot="actions" variant="outline" size="sm">Add milestone</bf-button>
|
|
179
|
+
</bf-page-header>
|
|
180
|
+
</div>
|
|
181
|
+
`,
|
|
182
|
+
})
|
|
183
|
+
export class PageHeaderHeadingLevelExampleComponent {}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
## Desktop
|
|
187
|
+
|
|
188
|
+
`bf-page-header` is for desktop web pages only: it has no large-title collapse, no scroll-under surface and no safe-area padding. It sits at the top of the page's main column, under the app's top nav or beside its sidebar. On a nested page put a `bf-breadcrumb` in its `breadcrumb` slot; above a list or table, the page's `bf-toolbar` (search, filters, view switch) follows it as its own row, not inside the `actions` slot, which holds only actions on the page as a whole. Every empty slot collapses, so a header with only a title takes one line. Hover, focus and density come from the controls you project.
|
|
189
|
+
|
|
190
|
+
## Theming
|
|
191
|
+
|
|
192
|
+
- Title: `text-title` type, `text` color. Subtitle: `text-body` type, `text-muted` color.
|
|
193
|
+
- The header adds no background, border or shadow; the projected breadcrumb, badges and buttons theme themselves.
|
|
194
|
+
|
|
195
|
+
The actions row sits at the inline end: on the right, and on the left under `dir="rtl"`.
|
|
196
|
+
|
|
197
|
+
## Accessibility
|
|
198
|
+
|
|
199
|
+
The host renders a `<header>`; the title is an `<h1>` by default and an `<h2>` when `headingLevel` is `2`, so keep one `h1` per page. The subtitle is a plain paragraph after the heading. The header handles no keys and moves no focus: the breadcrumb's links and the actions keep their own names and keyboard behaviour, in reading order (breadcrumb, title, badges, actions). Give an icon-only action a `label`.
|
|
200
|
+
|
|
201
|
+
## Inputs
|
|
202
|
+
|
|
203
|
+
| Name | Type | Default | Description |
|
|
204
|
+
| -------------- | ------------------------------------- | ------- | ------------------------------------------------------------------------------- |
|
|
205
|
+
| `title` | `string` | `''` | The page title, rendered as the heading; empty to use the `title` slot instead. |
|
|
206
|
+
| `subtitle` | `string` | `''` | One line under the title, such as a status or a count. |
|
|
207
|
+
| `headingLevel` | `BfPageHeaderHeadingLevel` (`1 \| 2`) | `1` | Heading level of the title so the header fits the page outline. |
|
|
208
|
+
|
|
209
|
+
## Slots
|
|
210
|
+
|
|
211
|
+
- `breadcrumb`: The trail above the title, such as `bf-breadcrumb`.
|
|
212
|
+
- `meta`: Badges beside the title, wrapping under it when they do not fit.
|
|
213
|
+
- `actions`: The page's buttons and menu buttons at the inline end.
|
|
214
|
+
- `title`: Rich title content inside the heading, when the `title` input is empty.
|
|
215
|
+
|
|
216
|
+
## Notes
|
|
217
|
+
|
|
218
|
+
### Do
|
|
219
|
+
|
|
220
|
+
- Keep one primary action; put the rest in a ghost button or the "More actions" menu.
|
|
221
|
+
- Keep the subtitle to one short line, such as progress or a due date.
|
|
222
|
+
|
|
223
|
+
### Don't
|
|
224
|
+
|
|
225
|
+
- Don't use `bf-page-header` inside the Capacitor shell; that is `bf-header`.
|
|
226
|
+
- Don't put search fields or filters in `actions`; they belong in a `bf-toolbar` below the header.
|
|
227
|
+
|
|
228
|
+
## Migration notes
|
|
229
|
+
|
|
230
|
+
### 0.3.1
|
|
231
|
+
|
|
232
|
+
New `bf-page-header` in `@bitforgehq/ui`, the desktop page header: a `breadcrumb` slot above,
|
|
233
|
+
the `title` as an `h1` (`headingLevel` 2 for an `h2`, `BfPageHeaderHeadingLevel`) with an
|
|
234
|
+
optional `subtitle`, a `meta` slot for badges beside the title, a `title` slot for rich title
|
|
235
|
+
content, and an `actions` slot at the inline end that wraps under the title in a narrow window.
|
|
236
|
+
Empty slots take no space. It is a sibling of `bf-header` (`/mobile`), not a shell-aware mode of
|
|
237
|
+
it: no large title, no scroll-under surface, no safe areas.
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
# bf-pagination
|
|
2
|
+
|
|
3
|
+
Previous, next and numbered page buttons for a long list or table
|
|
4
|
+
|
|
5
|
+
`BfPaginationComponent` from `@bitforgehq/ui` · Navigation · since 0.3.1
|
|
6
|
+
|
|
7
|
+
## Example
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { Component, computed, signal } from '@angular/core';
|
|
11
|
+
import { BfPaginationComponent } from '@bitforgehq/ui';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The bf-pagination overview.
|
|
15
|
+
*
|
|
16
|
+
* Live demo of `bf-pagination`: twelve pages of search results, the first and last page always
|
|
17
|
+
* shown, one page on each side of the current one and an ellipsis for the rest. The result line
|
|
18
|
+
* reports the page the buttons set.
|
|
19
|
+
*/
|
|
20
|
+
@Component({
|
|
21
|
+
selector: 'app-example-pagination-overview',
|
|
22
|
+
imports: [BfPaginationComponent],
|
|
23
|
+
template: `
|
|
24
|
+
<div class="flex flex-col gap-3">
|
|
25
|
+
<bf-pagination label="Result pages" [(page)]="page" [pageCount]="12" />
|
|
26
|
+
<p data-status aria-live="polite" class="text-caption text-text-muted">{{ status() }}</p>
|
|
27
|
+
</div>
|
|
28
|
+
`,
|
|
29
|
+
})
|
|
30
|
+
export class PaginationOverviewExampleComponent {
|
|
31
|
+
readonly page = signal(1);
|
|
32
|
+
|
|
33
|
+
readonly status = computed(() => {
|
|
34
|
+
const first = (this.page() - 1) * 20 + 1;
|
|
35
|
+
return `Page ${this.page()}: results ${first}–${first + 19} of 240.`;
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Overview
|
|
41
|
+
|
|
42
|
+
Page navigation for a long list or table: Previous and Next buttons around the page numbers,
|
|
43
|
+
the current one marked, the hidden runs shown as an ellipsis. `compact` shows "Page 3 of 12"
|
|
44
|
+
between the arrows instead, for narrow screens. The page count comes from `pageCount`, or from
|
|
45
|
+
`total` and `pageSize`; the consumer shows the rows of `page`.
|
|
46
|
+
|
|
47
|
+
Pagination splits a long list or table into pages and lets the user move between them: Previous and Next buttons around the page numbers, the current page marked, the first and the last page always in reach, and an ellipsis for each run of pages in between. `bf-pagination` holds only the page number. You bind `[(page)]` and show that page's rows, sliced on the client or fetched from the server. It works the same in both shells: in a desktop web app and in the Capacitor shell.
|
|
48
|
+
|
|
49
|
+
## When to use
|
|
50
|
+
|
|
51
|
+
Use `bf-pagination` under a table or a list of results the user moves through in steps and refers back to by position ("it was on page 3"): invoices, search results, an audit log. Use a "Load more" [bf-button](https://bitforge-ui.web.app/components/button) at the end of the list instead when the user only reads on, as in a feed, and in the Capacitor shell for a phone list that scrolls. Use [bf-tab-bar](https://bitforge-ui.web.app/components/tab-bar) or a [bf-top-nav](https://bitforge-ui.web.app/components/top-nav) for the sections of an app, not pagination.
|
|
52
|
+
|
|
53
|
+
## Page count
|
|
54
|
+
|
|
55
|
+
The number of pages comes from `pageCount`, or from `total` (the number of items) and `pageSize` (items per page, default 10), as `Math.ceil(total / pageSize)`. When `pageCount` is above 0 it wins and `total` is ignored; with neither set, or no items, there is one page. The count is never below 1, so an empty table still shows "1" with both arrows disabled.
|
|
56
|
+
|
|
57
|
+
`page` is 1-based and starts at 1. A `page` beyond the count shows as the last page (and below 1 as the first), but the component never writes the clamped value back: when the total shrinks, after a filter or a delete, set `page` back in range yourself.
|
|
58
|
+
|
|
59
|
+
## Pages around the current one
|
|
60
|
+
|
|
61
|
+
The row always shows the first and the last page, the current page and `siblings` pages on each side of it (default 1). Every run of hidden pages becomes an ellipsis; a run of only one page shows that page instead. So the row keeps the same number of slots (`2 × siblings + 5`) wherever the current page is, and the buttons do not shift under the pointer as the user steps through. Set `siblings` to `0` for the shortest row, or to `2` on a wide page. With few pages every page shows.
|
|
62
|
+
|
|
63
|
+
## Compact
|
|
64
|
+
|
|
65
|
+
`compact` replaces the page buttons with the position as text, "Page 3 of 12" (`provideBfLabels()`'s `pageOf`), between the arrows. Use it where the full row does not fit: a narrow column, a phone, a side panel. A responsive page can render the full row from `md` up and the compact one below it (a `hidden md:block` and a `md:hidden` wrapper around two `bf-pagination`s bound to the same `page`).
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
import { Component, signal } from '@angular/core';
|
|
69
|
+
import { BfPaginationComponent } from '@bitforgehq/ui';
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Compact pagination for a narrow space.
|
|
73
|
+
*
|
|
74
|
+
* `compact` shows "Page 3 of 12" between the arrows instead of the page buttons; the text is a
|
|
75
|
+
* polite live region, so each step is announced.
|
|
76
|
+
*/
|
|
77
|
+
@Component({
|
|
78
|
+
selector: 'app-example-pagination-compact',
|
|
79
|
+
imports: [BfPaginationComponent],
|
|
80
|
+
template: `<bf-pagination
|
|
81
|
+
label="Compact result pages"
|
|
82
|
+
compact
|
|
83
|
+
[(page)]="page"
|
|
84
|
+
[pageCount]="12"
|
|
85
|
+
/>`,
|
|
86
|
+
})
|
|
87
|
+
export class PaginationCompactExampleComponent {
|
|
88
|
+
readonly page = signal(3);
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## With a table
|
|
93
|
+
|
|
94
|
+
Put the pagination under the table it pages, aligned to the inline end or centred, and show only the current page's rows: slice them in a `computed()` from `page`, or fetch the page from the server in a `httpResource()` keyed on it. When the user sorts or filters, set `page` back to 1. The [table](https://bitforge-ui.web.app/components/table) page has a paged example.
|
|
95
|
+
|
|
96
|
+
## Sizes
|
|
97
|
+
|
|
98
|
+
`size` sets the button height: `sm` 32 px, `md` 40 px (default), `lg` 48 px; the page buttons are at least as wide as they are tall and grow for long numbers. Unset, it follows `provideBfDefaults()`'s `controlSize`, so a dense page's `{ controlSize: 'sm' }` makes it small with the rest of the page. Under a coarse pointer every button's hit area extends to 44 px tall without changing its look. When the row wraps (a narrow container), the lines keep 8 px apart, so the extended hit areas never cover a button on the next line.
|
|
99
|
+
|
|
100
|
+
## Desktop
|
|
101
|
+
|
|
102
|
+
The page buttons take the quiet ghost look: no fill at rest, `surface-muted` on hover, a step darker while pressed; the arrows are text icon buttons with a CSS chevron. The current page takes the `primary-soft` fill. The pointer cursor shows on every live button, the not-allowed cursor on an arrow at the end of the range. The focus ring shows on keyboard focus only. The arrows point to the inline start and end, so they mirror under `dir="rtl"`. Inside `provideBfShell()` (the Capacitor shell) it renders the same; prefer `compact` on a phone.
|
|
103
|
+
|
|
104
|
+
## Theming
|
|
105
|
+
|
|
106
|
+
- Page buttons: `text-muted` with a transparent fill; hover `surface-muted` and `text`, pressed `surface-pressed`; the current page `primary-soft` with `primary-strong` text, hover `primary-soft-hover`. Under forced colours the current page is outlined.
|
|
107
|
+
- Arrows: `text-muted` with a transparent fill, hover `surface-muted`, pressed `primary-soft-pressed`; at the ends `text-disabled`. The chevron is drawn with 1.5 px borders in the text colour.
|
|
108
|
+
- Ellipsis and the compact "Page 3 of 12": `text-muted`, bold, tabular figures.
|
|
109
|
+
- Shape: `rounded-control` buttons, the `ring` focus ring. Type: `text-label-xs`, `text-label-sm` and `text-label` per size.
|
|
110
|
+
|
|
111
|
+
## Accessibility
|
|
112
|
+
|
|
113
|
+
The host renders a `nav` landmark named by `label`, else "Pagination" (`provideBfLabels()`'s `pagination`), around a list of native `button`s; give each pagination on a page its own `label` ("Invoice pages"). A page button reads as "Page 4" (`goToPage`), so its visible number is part of its name, and the current page carries `aria-current="page"`. The ellipses are `aria-hidden`, so the list announces only the buttons. Previous and Next are named "Previous page" and "Next page" (`previousPage`, `nextPage`); the chevrons are `aria-hidden`.
|
|
114
|
+
|
|
115
|
+
At the first and the last page the arrow is disabled with `aria-disabled="true"`, not native `disabled`: it stays focusable and does nothing when pressed. This follows the WAI-ARIA guidance for controls that turn unavailable while focused: a user who presses Next until the last page keeps focus on the button instead of losing it to the page. In `compact` mode the "Page 3 of 12" text is a polite live region, so the new position is announced after each step; in the full row the pressed page button becomes the current one in place. Moving to another page does not move focus: the content that changed is the consumer's, so announce a new result count there if it matters.
|
|
116
|
+
|
|
117
|
+
### Keyboard
|
|
118
|
+
|
|
119
|
+
| Key | Action |
|
|
120
|
+
| ------------------------------------------------ | -------------------------------------------------------------------------- |
|
|
121
|
+
| <kbd>Tab</kbd> / <kbd>Shift</kbd>+<kbd>Tab</kbd> | Moves through Previous, the page buttons and Next, including disabled ends |
|
|
122
|
+
| <kbd>Enter</kbd> / <kbd>Space</kbd> | Goes to the focused page, or to the previous or next one |
|
|
123
|
+
|
|
124
|
+
## Inputs
|
|
125
|
+
|
|
126
|
+
| Name | Type | Default | Description |
|
|
127
|
+
| ----------- | ----------------------------------------------------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
128
|
+
| `pageCount` | `number` | `0` | The number of pages; wins over `total` and `pageSize` when above 0. |
|
|
129
|
+
| `total` | `number` | `0` | The number of items, used with `pageSize` while `pageCount` is 0. |
|
|
130
|
+
| `pageSize` | `number` | `10` | Items per page, used with `total` while `pageCount` is 0. |
|
|
131
|
+
| `siblings` | `number` | `1` | How many pages to show on each side of the current one. |
|
|
132
|
+
| `compact` | `boolean` | `false` | Shows "Page 3 of 12" between the arrows instead of the page buttons, for narrow screens. |
|
|
133
|
+
| `size` | `BfSize \| undefined` (`'sm' \| 'md' \| 'lg' \| undefined`) | `undefined` | Button height step: sm 32, md 40, lg 48 px (44 px hit area under a coarse pointer); unset follows `provideBfDefaults()` (`controlSize`, `md` by default). |
|
|
134
|
+
| `label` | `string` | `''` | Accessible name of the navigation; empty falls back to `provideBfLabels()`'s `pagination` ("Pagination"). |
|
|
135
|
+
| `page` | `number` | `1` | The current page, 1-based; two-way bindable. The buttons set it; show the rows of this page. Two-way: `[(page)]`. |
|
|
136
|
+
|
|
137
|
+
## Methods
|
|
138
|
+
|
|
139
|
+
### `go(target: number): void`
|
|
140
|
+
|
|
141
|
+
Goes to a page: sets `page` when it is another page within 1…count; else does nothing.
|
|
142
|
+
|
|
143
|
+
| Parameter | Type | Description |
|
|
144
|
+
| --------- | -------- | -------------------------- |
|
|
145
|
+
| `target` | `number` | The 1-based page to go to. |
|
|
146
|
+
|
|
147
|
+
## Notes
|
|
148
|
+
|
|
149
|
+
### Do
|
|
150
|
+
|
|
151
|
+
- Show the current page's rows and nothing else, and go back to page 1 after a sort or a filter.
|
|
152
|
+
- Give every pagination a `label` when a page has more than one.
|
|
153
|
+
- Use `compact` in a narrow space, and keep the full row where it fits.
|
|
154
|
+
|
|
155
|
+
### Don't
|
|
156
|
+
|
|
157
|
+
- Don't page a short list that fits on one screen; show it whole.
|
|
158
|
+
- Don't use pagination to switch between sections or steps; it is for pages of one list.
|
|
159
|
+
|
|
160
|
+
## Migration notes
|
|
161
|
+
|
|
162
|
+
### 0.3.1
|
|
163
|
+
|
|
164
|
+
New `bf-pagination` in `@bitforgehq/ui`, page navigation for both shells: a `nav` (named by
|
|
165
|
+
`label`, else the new `pagination` label) with Previous and Next buttons around the page buttons.
|
|
166
|
+
`[(page)]` is 1-based; the count comes from `pageCount`, else `total` over `pageSize` (default 10).
|
|
167
|
+
The row keeps the first and the last page and `siblings` (default 1) on each side of the current
|
|
168
|
+
one, which carries `aria-current="page"`, and turns hidden runs into an `aria-hidden` ellipsis, at
|
|
169
|
+
a constant number of slots. `compact` shows "Page 3 of 12" (`pageOf`) between the arrows as a
|
|
170
|
+
polite live region. At the ends the arrows are `aria-disabled` and stay focusable. `size` follows
|
|
171
|
+
`controlSize`; the chevrons are CSS and mirror under `dir="rtl"`. New `BfPaginationHarness` in
|
|
172
|
+
`@bitforgehq/ui/testing` (`getCurrentPage()`, `getPageCount()`, `getVisiblePages()`, `goTo()`,
|
|
173
|
+
`next()`, `previous()`).
|