@bitforgehq/ui 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +3665 -0
- package/LICENSE +21 -0
- package/README.md +175 -0
- package/ai/MANIFEST.index.json +383 -0
- package/ai/MANIFEST.json +7528 -0
- package/ai/REFERENCE.index.json +1461 -0
- package/ai/REFERENCE.json +7213 -0
- package/ai/UI_GUIDE.md +1159 -0
- package/ai/components/action-bar.md +209 -0
- package/ai/components/action-sheet.md +526 -0
- package/ai/components/alert-dialog.md +509 -0
- package/ai/components/alert.md +376 -0
- package/ai/components/avatar.md +146 -0
- package/ai/components/back-button.md +286 -0
- package/ai/components/badge.md +171 -0
- package/ai/components/button.md +431 -0
- package/ai/components/card.md +303 -0
- package/ai/components/checkbox.md +418 -0
- package/ai/components/chip-group.md +205 -0
- package/ai/components/chip.md +300 -0
- package/ai/components/content.md +474 -0
- package/ai/components/desktop-toast.md +420 -0
- package/ai/components/dialog.md +962 -0
- package/ai/components/empty-state.md +146 -0
- package/ai/components/field.md +319 -0
- package/ai/components/header.md +809 -0
- package/ai/components/icon-button.md +221 -0
- package/ai/components/icon.md +176 -0
- package/ai/components/input.md +455 -0
- package/ai/components/list-item.md +326 -0
- package/ai/components/list.md +178 -0
- package/ai/components/menu-button.md +377 -0
- package/ai/components/menu.md +661 -0
- package/ai/components/modal.md +613 -0
- package/ai/components/number-input.md +423 -0
- package/ai/components/page.md +299 -0
- package/ai/components/progress.md +215 -0
- package/ai/components/radio-group.md +432 -0
- package/ai/components/refresher.md +377 -0
- package/ai/components/segmented.md +517 -0
- package/ai/components/select.md +510 -0
- package/ai/components/sheet.md +845 -0
- package/ai/components/spinner.md +171 -0
- package/ai/components/stack-outlet.md +661 -0
- package/ai/components/tab-bar.md +427 -0
- package/ai/components/tab.md +295 -0
- package/ai/components/tabs.md +408 -0
- package/ai/components/textarea.md +371 -0
- package/ai/components/toast.md +522 -0
- package/ai/components/toggle.md +387 -0
- package/ai/reference/bf-action-directive.md +21 -0
- package/ai/reference/bf-action-sheet-action-role.md +27 -0
- package/ai/reference/bf-action-sheet-action.md +28 -0
- package/ai/reference/bf-action-sheet-options.md +28 -0
- package/ai/reference/bf-action-sheet-service.md +33 -0
- package/ai/reference/bf-alert-dialog-button-role.md +27 -0
- package/ai/reference/bf-alert-dialog-button.md +27 -0
- package/ai/reference/bf-alert-dialog-options.md +29 -0
- package/ai/reference/bf-alert-dialog-service.md +34 -0
- package/ai/reference/bf-alert-variant.md +28 -0
- package/ai/reference/bf-autocapitalize.md +30 -0
- package/ai/reference/bf-avatar-size.md +27 -0
- package/ai/reference/bf-avatar-variant.md +28 -0
- package/ai/reference/bf-back-at-root.md +28 -0
- package/ai/reference/bf-back-button-service.md +40 -0
- package/ai/reference/bf-badge-variant.md +29 -0
- package/ai/reference/bf-button-size.md +29 -0
- package/ai/reference/bf-button-type.md +28 -0
- package/ai/reference/bf-button-variant.md +29 -0
- package/ai/reference/bf-card-padding.md +26 -0
- package/ai/reference/bf-card-variant.md +28 -0
- package/ai/reference/bf-chip-group-mode.md +27 -0
- package/ai/reference/bf-chip-variant.md +26 -0
- package/ai/reference/bf-compare-with.md +24 -0
- package/ai/reference/bf-component-overlay-ref.md +33 -0
- package/ai/reference/bf-confirm-button-role.md +26 -0
- package/ai/reference/bf-confirm-button.md +27 -0
- package/ai/reference/bf-confirm-options.md +31 -0
- package/ai/reference/bf-confirm-size.md +26 -0
- package/ai/reference/bf-content-scroll-event.md +21 -0
- package/ai/reference/bf-defaults-options.md +32 -0
- package/ai/reference/bf-defaults.md +28 -0
- package/ai/reference/bf-desktop-toast-config.md +28 -0
- package/ai/reference/bf-desktop-toast-harness.md +33 -0
- package/ai/reference/bf-desktop-toast-options.md +28 -0
- package/ai/reference/bf-desktop-toast-position.md +28 -0
- package/ai/reference/bf-desktop-toast-service.md +42 -0
- package/ai/reference/bf-dialog-config.md +44 -0
- package/ai/reference/bf-dialog-harness.md +52 -0
- package/ai/reference/bf-dialog-ref.md +44 -0
- package/ai/reference/bf-dialog-role.md +26 -0
- package/ai/reference/bf-dialog-service.md +55 -0
- package/ai/reference/bf-dialog-size.md +31 -0
- package/ai/reference/bf-dialog-template-context.md +32 -0
- package/ai/reference/bf-empty-state-heading-level.md +26 -0
- package/ai/reference/bf-enter-key-hint.md +31 -0
- package/ai/reference/bf-field-control-class.md +32 -0
- package/ai/reference/bf-field-control-state.md +28 -0
- package/ai/reference/bf-field-size.md +29 -0
- package/ai/reference/bf-haptics-impact-style.md +27 -0
- package/ai/reference/bf-haptics-notification-type.md +27 -0
- package/ai/reference/bf-haptics-service.md +41 -0
- package/ai/reference/bf-harness-filters.md +29 -0
- package/ai/reference/bf-horizontal-scroll-directive.md +129 -0
- package/ai/reference/bf-icon-button-variant.md +28 -0
- package/ai/reference/bf-icon-size.md +27 -0
- package/ai/reference/bf-input-autocapitalize.md +36 -0
- package/ai/reference/bf-input-enter-key-hint.md +38 -0
- package/ai/reference/bf-input-input-mode.md +38 -0
- package/ai/reference/bf-input-mode.md +32 -0
- package/ai/reference/bf-input-type.md +35 -0
- package/ai/reference/bf-jsdom-shim-options.md +22 -0
- package/ai/reference/bf-keyboard-assist-behavior.md +26 -0
- package/ai/reference/bf-keyboard-assist-directive.md +185 -0
- package/ai/reference/bf-keyboard-phase.md +30 -0
- package/ai/reference/bf-keyboard-service.md +50 -0
- package/ai/reference/bf-labels-overrides.md +26 -0
- package/ai/reference/bf-labels.md +44 -0
- package/ai/reference/bf-menu-button-harness.md +40 -0
- package/ai/reference/bf-menu-button-variant.md +27 -0
- package/ai/reference/bf-menu-divider-directive.md +13 -0
- package/ai/reference/bf-menu-group-directive.md +14 -0
- package/ai/reference/bf-menu-harness.md +55 -0
- package/ai/reference/bf-menu-item-checkbox-directive.md +31 -0
- package/ai/reference/bf-menu-item-directive.md +31 -0
- package/ai/reference/bf-menu-item-harness.md +52 -0
- package/ai/reference/bf-menu-item-radio-directive.md +29 -0
- package/ai/reference/bf-menu-placement.md +29 -0
- package/ai/reference/bf-menu-trigger-directive.md +46 -0
- package/ai/reference/bf-modal-options.md +30 -0
- package/ai/reference/bf-modal-presentation.md +26 -0
- package/ai/reference/bf-modal-service.md +38 -0
- package/ai/reference/bf-nav-controller.md +59 -0
- package/ai/reference/bf-nav-direction.md +23 -0
- package/ai/reference/bf-nav-target.md +21 -0
- package/ai/reference/bf-number-input-mode.md +28 -0
- package/ai/reference/bf-number-input-step.md +25 -0
- package/ai/reference/bf-overlay-auto-focus.md +29 -0
- package/ai/reference/bf-overlay-controller.md +22 -0
- package/ai/reference/bf-overlay-host.md +36 -0
- package/ai/reference/bf-overlay-options.md +53 -0
- package/ai/reference/bf-overlay-position.md +30 -0
- package/ai/reference/bf-overlay-ref.md +53 -0
- package/ai/reference/bf-overlay-role.md +27 -0
- package/ai/reference/bf-overlay-service.md +53 -0
- package/ai/reference/bf-overlay-stack-options.md +24 -0
- package/ai/reference/bf-overlay-stack.md +81 -0
- package/ai/reference/bf-overlay-template-context.md +24 -0
- package/ai/reference/bf-overscroll-stretch-directive.md +163 -0
- package/ai/reference/bf-page-context.md +24 -0
- package/ai/reference/bf-platform-service.md +53 -0
- package/ai/reference/bf-platform.md +28 -0
- package/ai/reference/bf-progress-size.md +26 -0
- package/ai/reference/bf-progress-tone.md +28 -0
- package/ai/reference/bf-radio-group-harness.md +49 -0
- package/ai/reference/bf-radio-group-orientation.md +26 -0
- package/ai/reference/bf-radio-option.md +30 -0
- package/ai/reference/bf-refresher-refresh-event.md +22 -0
- package/ai/reference/bf-segment-option.md +29 -0
- package/ai/reference/bf-segmented-harness.md +48 -0
- package/ai/reference/bf-segmented-size.md +29 -0
- package/ai/reference/bf-select-harness.md +70 -0
- package/ai/reference/bf-select-option.md +29 -0
- package/ai/reference/bf-sheet-auto-focus.md +29 -0
- package/ai/reference/bf-sheet-options.md +32 -0
- package/ai/reference/bf-sheet-ref.md +45 -0
- package/ai/reference/bf-sheet-service.md +34 -0
- package/ai/reference/bf-shell-config-mode.md +28 -0
- package/ai/reference/bf-shell-config.md +38 -0
- package/ai/reference/bf-shell-dynamic-type-limit.md +21 -0
- package/ai/reference/bf-shell-dynamic-type.md +25 -0
- package/ai/reference/bf-shell-mode.md +28 -0
- package/ai/reference/bf-shell-testing-options.md +26 -0
- package/ai/reference/bf-size.md +45 -0
- package/ai/reference/bf-spinner-size.md +27 -0
- package/ai/reference/bf-stack-change.md +22 -0
- package/ai/reference/bf-stack-reuse-strategy.md +32 -0
- package/ai/reference/bf-stack-transition-event.md +26 -0
- package/ai/reference/bf-stack-transition.md +26 -0
- package/ai/reference/bf-stacked-overlay.md +23 -0
- package/ai/reference/bf-system-bars-style.md +38 -0
- package/ai/reference/bf-tab-bar-controller.md +51 -0
- package/ai/reference/bf-tab-bar-host.md +52 -0
- package/ai/reference/bf-tab-context.md +32 -0
- package/ai/reference/bf-tab-links.md +37 -0
- package/ai/reference/bf-testing-options.md +27 -0
- package/ai/reference/bf-text-scale-service.md +35 -0
- package/ai/reference/bf-theme.md +27 -0
- package/ai/reference/bf-toast-action.md +25 -0
- package/ai/reference/bf-toast-controller.md +17 -0
- package/ai/reference/bf-toast-dismiss-reason.md +29 -0
- package/ai/reference/bf-toast-options.md +29 -0
- package/ai/reference/bf-toast-position.md +26 -0
- package/ai/reference/bf-toast-ref.md +41 -0
- package/ai/reference/bf-toast-service.md +39 -0
- package/ai/reference/bf-toggle-label-position.md +26 -0
- package/ai/reference/bf-transition-kind.md +28 -0
- package/ai/reference/bf-validation-message.md +31 -0
- package/ai/reference/bf-validation-messages.md +33 -0
- package/ai/reference/bf-view-event.md +28 -0
- package/ai/reference/bf-view-lifecycle.md +41 -0
- package/ai/reference/bf-view-state.md +28 -0
- package/ai/reference/bf_default_labels.md +21 -0
- package/ai/reference/bf_default_values.md +21 -0
- package/ai/reference/bf_defaults.md +21 -0
- package/ai/reference/bf_desktop_toast_defaults.md +21 -0
- package/ai/reference/bf_dialog_data.md +17 -0
- package/ai/reference/bf_labels.md +21 -0
- package/ai/reference/bf_overlay_data.md +19 -0
- package/ai/reference/bf_overlay_host.md +21 -0
- package/ai/reference/bf_sheet_default_snap_points.md +17 -0
- package/ai/reference/bf_shell_config.md +21 -0
- package/ai/reference/bf_shell_defaults.md +21 -0
- package/ai/reference/bf_tab_bar.md +21 -0
- package/ai/reference/bf_tab_bar_controller.md +21 -0
- package/ai/reference/bf_tab_context.md +21 -0
- package/ai/reference/bf_tab_links.md +21 -0
- package/ai/reference/bf_theme_storage.md +19 -0
- package/ai/reference/create-transition.md +36 -0
- package/ai/reference/duration_fast.md +17 -0
- package/ai/reference/duration_md.md +17 -0
- package/ai/reference/duration_overlay.md +17 -0
- package/ai/reference/duration_push.md +17 -0
- package/ai/reference/fade-down.md +26 -0
- package/ai/reference/fade-in.md +25 -0
- package/ai/reference/fade-out.md +25 -0
- package/ai/reference/fade-up.md +26 -0
- package/ai/reference/inject-shell-mode.md +23 -0
- package/ai/reference/install-bf-jsdom-shims.md +31 -0
- package/ai/reference/ios_duration.md +17 -0
- package/ai/reference/ios_easing.md +17 -0
- package/ai/reference/is-text-entry.md +25 -0
- package/ai/reference/max_complete_ms.md +17 -0
- package/ai/reference/md-transition.md +31 -0
- package/ai/reference/md_duration.md +17 -0
- package/ai/reference/md_easing.md +17 -0
- package/ai/reference/overlay_duration.md +21 -0
- package/ai/reference/overlay_easing.md +19 -0
- package/ai/reference/overlay_reduced_duration.md +17 -0
- package/ai/reference/pop-frames-at.md +25 -0
- package/ai/reference/prefers-reduced-motion.md +25 -0
- package/ai/reference/provide-bf-defaults.md +33 -0
- package/ai/reference/provide-bf-desktop-toast.md +28 -0
- package/ai/reference/provide-bf-labels.md +38 -0
- package/ai/reference/provide-bf-shell-testing.md +34 -0
- package/ai/reference/provide-bf-shell.md +41 -0
- package/ai/reference/provide-bf-testing.md +33 -0
- package/ai/reference/reduced-transition.md +28 -0
- package/ai/reference/reduced_duration.md +17 -0
- package/ai/reference/resolve-shell-mode.md +33 -0
- package/ai/reference/scale-in.md +25 -0
- package/ai/reference/settle-duration.md +25 -0
- package/ai/reference/settle-pop.md +33 -0
- package/ai/reference/settle_easing.md +17 -0
- package/ai/reference/should-complete-pop.md +26 -0
- package/ai/reference/slide-down.md +26 -0
- package/ai/reference/slide-up.md +26 -0
- package/ai/reference/with-bf-shell-routing.md +19 -0
- package/bin/bf-ui.js +81 -0
- package/fesm2022/bitforgehq-ui-icons.mjs +230 -0
- package/fesm2022/bitforgehq-ui-icons.mjs.map +1 -0
- package/fesm2022/bitforgehq-ui-mobile.mjs +5530 -0
- package/fesm2022/bitforgehq-ui-mobile.mjs.map +1 -0
- package/fesm2022/bitforgehq-ui-number-input.mjs +680 -0
- package/fesm2022/bitforgehq-ui-number-input.mjs.map +1 -0
- package/fesm2022/bitforgehq-ui-overlay.mjs +2675 -0
- package/fesm2022/bitforgehq-ui-overlay.mjs.map +1 -0
- package/fesm2022/bitforgehq-ui-testing.mjs +402 -0
- package/fesm2022/bitforgehq-ui-testing.mjs.map +1 -0
- package/fesm2022/bitforgehq-ui.mjs +7765 -0
- package/fesm2022/bitforgehq-ui.mjs.map +1 -0
- package/llms-full.txt +25394 -0
- package/llms.txt +329 -0
- package/package.json +94 -0
- package/theme.css +253 -0
- package/types/bitforgehq-ui-icons.d.ts +50 -0
- package/types/bitforgehq-ui-mobile.d.ts +2914 -0
- package/types/bitforgehq-ui-number-input.d.ts +282 -0
- package/types/bitforgehq-ui-overlay.d.ts +1514 -0
- package/types/bitforgehq-ui-testing.d.ts +495 -0
- package/types/bitforgehq-ui.d.ts +3499 -0
|
@@ -0,0 +1,510 @@
|
|
|
1
|
+
# bf-select
|
|
2
|
+
|
|
3
|
+
Native single-choice dropdown styled like the text field, with Signal Forms binding
|
|
4
|
+
|
|
5
|
+
`BfSelectComponent` from `@bitforgehq/ui` · Forms · since 0.1.0
|
|
6
|
+
|
|
7
|
+
## Example
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import { Component, computed, signal } from '@angular/core';
|
|
11
|
+
import { BfButtonComponent, BfSelectComponent, type BfSelectOption } from '@bitforgehq/ui';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* The bf-select overview.
|
|
15
|
+
*
|
|
16
|
+
* Live demo of `bf-select`: a placeholder option and three real options, bound to a signal, with a
|
|
17
|
+
* number-valued team select and a Clear button that sets `null`; then the sm, md and lg sizes in a
|
|
18
|
+
* row.
|
|
19
|
+
*/
|
|
20
|
+
@Component({
|
|
21
|
+
selector: 'app-example-select-overview',
|
|
22
|
+
imports: [BfButtonComponent, BfSelectComponent],
|
|
23
|
+
template: `
|
|
24
|
+
<div class="flex flex-col gap-3">
|
|
25
|
+
<bf-select
|
|
26
|
+
label="Region"
|
|
27
|
+
placeholder="Pick a region"
|
|
28
|
+
[options]="regions"
|
|
29
|
+
[(value)]="region"
|
|
30
|
+
/>
|
|
31
|
+
<div class="flex flex-wrap items-end gap-3">
|
|
32
|
+
<bf-select
|
|
33
|
+
label="Team"
|
|
34
|
+
placeholder="Pick a team"
|
|
35
|
+
class="w-56"
|
|
36
|
+
[options]="teams"
|
|
37
|
+
[(value)]="teamId"
|
|
38
|
+
/>
|
|
39
|
+
<bf-button variant="outline" (click)="teamId.set(null)">Clear</bf-button>
|
|
40
|
+
</div>
|
|
41
|
+
<p data-status aria-live="polite" class="text-caption text-text-muted">{{ status() }}</p>
|
|
42
|
+
<div data-sizes class="flex flex-wrap items-center gap-3">
|
|
43
|
+
<bf-select
|
|
44
|
+
size="sm"
|
|
45
|
+
ariaLabel="Small select"
|
|
46
|
+
placeholder="sm · 32 px"
|
|
47
|
+
[options]="regions"
|
|
48
|
+
class="w-40"
|
|
49
|
+
/>
|
|
50
|
+
<bf-select
|
|
51
|
+
ariaLabel="Medium select"
|
|
52
|
+
placeholder="md · 40 px"
|
|
53
|
+
[options]="regions"
|
|
54
|
+
class="w-40"
|
|
55
|
+
/>
|
|
56
|
+
<bf-select
|
|
57
|
+
size="lg"
|
|
58
|
+
ariaLabel="Large select"
|
|
59
|
+
placeholder="lg · 48 px"
|
|
60
|
+
[options]="regions"
|
|
61
|
+
class="w-40"
|
|
62
|
+
/>
|
|
63
|
+
</div>
|
|
64
|
+
</div>
|
|
65
|
+
`,
|
|
66
|
+
})
|
|
67
|
+
export class SelectOverviewExampleComponent {
|
|
68
|
+
readonly regions: BfSelectOption[] = [
|
|
69
|
+
{ value: 'ch', label: 'Switzerland' },
|
|
70
|
+
{ value: 'de', label: 'Germany' },
|
|
71
|
+
{ value: 'fr', label: 'France' },
|
|
72
|
+
];
|
|
73
|
+
readonly region = signal<string | null>(null);
|
|
74
|
+
/** Numeric ids: the value is a `number`, `null` while nothing is picked. */
|
|
75
|
+
readonly teams: BfSelectOption<number>[] = [
|
|
76
|
+
{ value: 7, label: 'Design' },
|
|
77
|
+
{ value: 42, label: 'Platform' },
|
|
78
|
+
];
|
|
79
|
+
readonly teamId = signal<number | null>(null);
|
|
80
|
+
|
|
81
|
+
readonly status = computed(() => {
|
|
82
|
+
const region = this.regions.find((r) => r.value === this.region());
|
|
83
|
+
const team = this.teamId();
|
|
84
|
+
return [
|
|
85
|
+
region ? `Region: ${region.label}` : 'No region picked.',
|
|
86
|
+
team === null ? 'No team (null).' : `Team id: ${team}.`,
|
|
87
|
+
].join(' ');
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Overview
|
|
93
|
+
|
|
94
|
+
Native single-choice dropdown styled like the text field, with a placeholder option and
|
|
95
|
+
Signal Forms binding. Generic over the option value (a string by default); `null` means nothing
|
|
96
|
+
is selected.
|
|
97
|
+
|
|
98
|
+
A single-choice dropdown built on the native `<select>`, so it opens the platform picker on phones and stays keyboard-accessible everywhere. Styled like the text field with a CSS chevron.
|
|
99
|
+
|
|
100
|
+
## When to use
|
|
101
|
+
|
|
102
|
+
Use `bf-select` to choose one value from a list too long to show at once: a country, a project, a time zone. Use [bf-radio-group](https://bitforge-ui.web.app/components/radio-group) instead for up to five or six options that should be read side by side, [bf-segmented](https://bitforge-ui.web.app/components/segmented) for two to four options that switch a view, and [bf-menu-button](https://bitforge-ui.web.app/components/menu-button) for actions rather than a value.
|
|
103
|
+
|
|
104
|
+
## Basic usage
|
|
105
|
+
|
|
106
|
+
```ts
|
|
107
|
+
import { Component, signal } from '@angular/core';
|
|
108
|
+
import { BfSelectComponent, type BfSelectOption } from '@bitforgehq/ui';
|
|
109
|
+
|
|
110
|
+
/** A select of projects with a placeholder until one is picked. */
|
|
111
|
+
@Component({
|
|
112
|
+
selector: 'app-example-select-basic',
|
|
113
|
+
imports: [BfSelectComponent],
|
|
114
|
+
template: `
|
|
115
|
+
<div class="flex flex-col gap-2">
|
|
116
|
+
<bf-select
|
|
117
|
+
label="Project"
|
|
118
|
+
placeholder="Pick a project"
|
|
119
|
+
[options]="projects"
|
|
120
|
+
[(value)]="projectId"
|
|
121
|
+
/>
|
|
122
|
+
<p class="text-caption text-text-muted">projectId: {{ projectId() ?? 'null' }}</p>
|
|
123
|
+
</div>
|
|
124
|
+
`,
|
|
125
|
+
})
|
|
126
|
+
export class SelectBasicExampleComponent {
|
|
127
|
+
readonly projects: BfSelectOption[] = [
|
|
128
|
+
{ value: 'atlas', label: 'Atlas rebrand' },
|
|
129
|
+
{ value: 'bloom', label: 'Bloom app' },
|
|
130
|
+
];
|
|
131
|
+
readonly projectId = signal<string | null>(null);
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
`value` is `null` while nothing is selected, and the placeholder shows then. `(changed)` emits the
|
|
136
|
+
chosen value (never `null`) after a user change.
|
|
137
|
+
|
|
138
|
+
## Typed values
|
|
139
|
+
|
|
140
|
+
The select is generic over the option value: numbers, ids and objects bind directly, with no
|
|
141
|
+
string conversion. `BfSelectOption` defaults to `string`; type it for anything else, and bind a
|
|
142
|
+
`T | null` signal or form field.
|
|
143
|
+
|
|
144
|
+
```ts
|
|
145
|
+
import { Component, signal } from '@angular/core';
|
|
146
|
+
import { BfSelectComponent, type BfSelectOption } from '@bitforgehq/ui';
|
|
147
|
+
|
|
148
|
+
/** A select of numeric team ids: the bound value is a `number | null`. */
|
|
149
|
+
@Component({
|
|
150
|
+
selector: 'app-example-select-typed',
|
|
151
|
+
imports: [BfSelectComponent],
|
|
152
|
+
template: `
|
|
153
|
+
<div class="flex flex-col gap-2">
|
|
154
|
+
<bf-select label="Team" placeholder="Pick a team" [options]="teams" [(value)]="teamId" />
|
|
155
|
+
<p class="text-caption text-text-muted">teamId: {{ teamId() ?? 'null' }}</p>
|
|
156
|
+
</div>
|
|
157
|
+
`,
|
|
158
|
+
})
|
|
159
|
+
export class SelectTypedExampleComponent {
|
|
160
|
+
readonly teams: BfSelectOption<number>[] = [
|
|
161
|
+
{ value: 7, label: 'Design' },
|
|
162
|
+
{ value: 42, label: 'Platform' },
|
|
163
|
+
];
|
|
164
|
+
readonly teamId = signal<number | null>(null);
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Options are matched to `value` with `Object.is`. For object values that are equal but not the same
|
|
169
|
+
instance (reloaded from a server), pass `compareWith`:
|
|
170
|
+
|
|
171
|
+
```ts
|
|
172
|
+
import { Component, signal } from '@angular/core';
|
|
173
|
+
import { FormField, form } from '@angular/forms/signals';
|
|
174
|
+
import { BfSelectComponent, type BfSelectOption } from '@bitforgehq/ui';
|
|
175
|
+
|
|
176
|
+
interface Team {
|
|
177
|
+
id: number;
|
|
178
|
+
name: string;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* A select of objects matched by id.
|
|
183
|
+
*
|
|
184
|
+
* The form's team is an equal object, not the option's instance (as after a reload), so
|
|
185
|
+
* `compareWith` matches by `id`.
|
|
186
|
+
*/
|
|
187
|
+
@Component({
|
|
188
|
+
selector: 'app-example-select-compare-with',
|
|
189
|
+
imports: [FormField, BfSelectComponent],
|
|
190
|
+
template: `
|
|
191
|
+
<bf-select label="Team" [options]="teamOptions" [compareWith]="byId" [formField]="task.team" />
|
|
192
|
+
`,
|
|
193
|
+
})
|
|
194
|
+
export class SelectCompareWithExampleComponent {
|
|
195
|
+
protected readonly teamOptions: BfSelectOption<Team>[] = [
|
|
196
|
+
{ value: { id: 7, name: 'Design' }, label: 'Design' },
|
|
197
|
+
{ value: { id: 42, name: 'Platform' }, label: 'Platform' },
|
|
198
|
+
];
|
|
199
|
+
readonly byId = (a: Team, b: Team) => a.id === b.id;
|
|
200
|
+
private readonly model = signal<{ team: Team | null }>({ team: { id: 42, name: 'Platform' } });
|
|
201
|
+
protected readonly task = form(this.model);
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
`compareWith` is called as `compareWith(optionValue, value)`. A string, number or boolean option
|
|
206
|
+
value is also the native `<option value>` (`"42"` for `42`), so a native form post and browser
|
|
207
|
+
tests (`selectOption('42')`) see it; an object option's native value is its index. A choice is
|
|
208
|
+
mapped back by position, so the model always gets the option's real value. An option whose value is `null` is not supported: `null` means nothing is selected, so such an
|
|
209
|
+
option never shows as selected.
|
|
210
|
+
|
|
211
|
+
Migrating from 0.2: "nothing selected" is `null` (it was `''`). Type bound signals and fields
|
|
212
|
+
`T | null` and compare against `null`, or listen to `(changed)`; an option whose value is `''`
|
|
213
|
+
(e.g. "All") is now a real option.
|
|
214
|
+
|
|
215
|
+
## With Signal Forms
|
|
216
|
+
|
|
217
|
+
```ts
|
|
218
|
+
import { Component, signal } from '@angular/core';
|
|
219
|
+
import { FormField, form } from '@angular/forms/signals';
|
|
220
|
+
import { BfSelectComponent, type BfSelectOption } from '@bitforgehq/ui';
|
|
221
|
+
|
|
222
|
+
/** A select bound to a Signal Forms field. */
|
|
223
|
+
@Component({
|
|
224
|
+
selector: 'app-example-select-signal-forms',
|
|
225
|
+
imports: [FormField, BfSelectComponent],
|
|
226
|
+
template: `<bf-select label="Region" [options]="regions" [formField]="profile.region" />`,
|
|
227
|
+
})
|
|
228
|
+
export class SelectSignalFormsExampleComponent {
|
|
229
|
+
protected readonly regions: BfSelectOption[] = [
|
|
230
|
+
{ value: 'eu', label: 'Europe' },
|
|
231
|
+
{ value: 'us', label: 'Americas' },
|
|
232
|
+
];
|
|
233
|
+
private readonly model = signal<{ region: string | null }>({ region: 'eu' });
|
|
234
|
+
protected readonly profile = form(this.model);
|
|
235
|
+
}
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
Model the field as `T | null`, starting at `null`; `required()` treats `null` as empty. With
|
|
239
|
+
Reactive Forms, bind a `FormControl<T | null>` through `[formControl]` or `formControlName`.
|
|
240
|
+
|
|
241
|
+
## Read-only
|
|
242
|
+
|
|
243
|
+
`readonly` shows the chosen option on the muted surface and blocks changes. A mouse press focuses
|
|
244
|
+
the select without opening the picker, and the picker keys (arrows, Alt+Arrow, Enter, Space, F4,
|
|
245
|
+
type-ahead) do nothing. On touch screens the platform picker can still open, because the browser
|
|
246
|
+
gives no press to cancel; a choice made there snaps back to the bound value, so the value never
|
|
247
|
+
changes. The select stays in the tab order. Signal Forms binds it from `readonly()` rules.
|
|
248
|
+
|
|
249
|
+
```ts
|
|
250
|
+
import { Component } from '@angular/core';
|
|
251
|
+
import { BfSelectComponent, type BfSelectOption } from '@bitforgehq/ui';
|
|
252
|
+
|
|
253
|
+
/** A read-only select. */
|
|
254
|
+
@Component({
|
|
255
|
+
selector: 'app-example-select-readonly',
|
|
256
|
+
imports: [BfSelectComponent],
|
|
257
|
+
template: `<bf-select label="Region" [options]="regions" [readonly]="true" [value]="region" />`,
|
|
258
|
+
})
|
|
259
|
+
export class SelectReadonlyExampleComponent {
|
|
260
|
+
protected readonly regions: BfSelectOption[] = [
|
|
261
|
+
{ value: 'eu', label: 'Europe' },
|
|
262
|
+
{ value: 'us', label: 'Americas' },
|
|
263
|
+
];
|
|
264
|
+
protected readonly region = 'us';
|
|
265
|
+
}
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
## Without a visible label
|
|
269
|
+
|
|
270
|
+
A table filter or a toolbar select can skip the visible `label`; give it `ariaLabel`, or
|
|
271
|
+
`ariaLabelledby` pointing at the element that names it. A select without a visible label needs one
|
|
272
|
+
of the two. Both are ignored while `label` is set.
|
|
273
|
+
|
|
274
|
+
```ts
|
|
275
|
+
import { Component, signal } from '@angular/core';
|
|
276
|
+
import { BfSelectComponent, type BfSelectOption } from '@bitforgehq/ui';
|
|
277
|
+
|
|
278
|
+
/** A small toolbar select named by `ariaLabel`. */
|
|
279
|
+
@Component({
|
|
280
|
+
selector: 'app-example-select-unlabelled',
|
|
281
|
+
imports: [BfSelectComponent],
|
|
282
|
+
template: `
|
|
283
|
+
<bf-select ariaLabel="Filter by status" size="sm" [options]="statuses" [(value)]="status" />
|
|
284
|
+
`,
|
|
285
|
+
})
|
|
286
|
+
export class SelectUnlabelledExampleComponent {
|
|
287
|
+
protected readonly statuses: BfSelectOption[] = [
|
|
288
|
+
{ value: 'all', label: 'All' },
|
|
289
|
+
{ value: 'open', label: 'Open' },
|
|
290
|
+
];
|
|
291
|
+
protected readonly status = signal<string | null>('all');
|
|
292
|
+
}
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
## Sizes
|
|
296
|
+
|
|
297
|
+
`sm` 32 px, `md` 40 px (default), `lg` 48 px — the `BfSize` scale shared with `bf-input`, `bf-button`, `bf-chip` and `bf-segmented`, so a toolbar of one step has one height. Unset, `size` follows `provideBfDefaults()`'s `fieldSize`, `md` by default; a Capacitor app sets `provideBfDefaults({ fieldSize: 'lg' })` once for the 48 px phone look, and a dense sub-tree (a filter bar, a table) can set `{ size: 'sm' }` in a component's `providers`. Fields get no touch extension: `sm`/`md` fields stay 32/40 px under a coarse pointer, so touch apps use `provideBfDefaults({ fieldSize: 'lg' })`. The chevron sits at the inline end (left under `dir="rtl"`).
|
|
298
|
+
|
|
299
|
+
```ts
|
|
300
|
+
import { Component, signal } from '@angular/core';
|
|
301
|
+
import { BfSelectComponent, type BfSelectOption } from '@bitforgehq/ui';
|
|
302
|
+
|
|
303
|
+
/** The sm, md and lg select sizes. */
|
|
304
|
+
@Component({
|
|
305
|
+
selector: 'app-example-select-sizes',
|
|
306
|
+
imports: [BfSelectComponent],
|
|
307
|
+
template: `
|
|
308
|
+
<div class="flex flex-col gap-3">
|
|
309
|
+
<bf-select size="sm" ariaLabel="Status" [options]="statuses" [(value)]="status" />
|
|
310
|
+
<bf-select label="Project" [options]="projects" [(value)]="projectId" />
|
|
311
|
+
<bf-select size="lg" label="Region" [options]="regions" [(value)]="region" />
|
|
312
|
+
</div>
|
|
313
|
+
`,
|
|
314
|
+
})
|
|
315
|
+
export class SelectSizesExampleComponent {
|
|
316
|
+
protected readonly statuses: BfSelectOption[] = [{ value: 'open', label: 'Open' }];
|
|
317
|
+
protected readonly projects: BfSelectOption[] = [{ value: 'atlas', label: 'Atlas rebrand' }];
|
|
318
|
+
protected readonly regions: BfSelectOption[] = [{ value: 'eu', label: 'Europe' }];
|
|
319
|
+
protected readonly status = signal<string | null>('open');
|
|
320
|
+
protected readonly projectId = signal<string | null>('atlas');
|
|
321
|
+
protected readonly region = signal<string | null>('eu');
|
|
322
|
+
}
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
## Validation
|
|
326
|
+
|
|
327
|
+
Once the control is touched it shows the first error as text: the error's own `message`, else the default for its kind from `provideBfLabels()`'s `validation` labels (`required` "Fill in this field", `email`, `pattern`, `parse`, `min(n)`, `max(n)`, `minLength(n)` "Use at least n characters", `maxLength(n)`, `minDate(d)`, `maxDate(d)`, and `fallback` "Check this value" for any other kind), so it never turns red without text. Reactive Forms `Validators.*` errors carry no message and get the same defaults (`minlength`/`maxlength` read their limit from the error's `context`). An explicit `errorMessage` wins over both and shows at once, e.g. for a server error.
|
|
328
|
+
|
|
329
|
+
```ts
|
|
330
|
+
import { Component, signal } from '@angular/core';
|
|
331
|
+
import { FormField, FormRoot, form, required } from '@angular/forms/signals';
|
|
332
|
+
import { BfButtonComponent, BfSelectComponent, type BfSelectOption } from '@bitforgehq/ui';
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* A required select with the default message.
|
|
336
|
+
*
|
|
337
|
+
* `required(p.region)` has no message, so a submit with nothing chosen shows "Fill in this field".
|
|
338
|
+
*/
|
|
339
|
+
@Component({
|
|
340
|
+
selector: 'app-example-select-validation',
|
|
341
|
+
imports: [FormField, FormRoot, BfButtonComponent, BfSelectComponent],
|
|
342
|
+
template: `
|
|
343
|
+
<form [formRoot]="profile" class="flex flex-col items-start gap-3">
|
|
344
|
+
<bf-select
|
|
345
|
+
label="Region"
|
|
346
|
+
class="self-stretch"
|
|
347
|
+
[options]="options"
|
|
348
|
+
[formField]="profile.region"
|
|
349
|
+
/>
|
|
350
|
+
<bf-button type="submit">Save</bf-button>
|
|
351
|
+
</form>
|
|
352
|
+
`,
|
|
353
|
+
})
|
|
354
|
+
export class SelectValidationExampleComponent {
|
|
355
|
+
protected readonly options: BfSelectOption[] = [
|
|
356
|
+
{ value: 'eu', label: 'Europe' },
|
|
357
|
+
{ value: 'us', label: 'Americas' },
|
|
358
|
+
];
|
|
359
|
+
private readonly model = signal<{ region: string | null }>({ region: null });
|
|
360
|
+
protected readonly profile = form(
|
|
361
|
+
this.model,
|
|
362
|
+
(p) => {
|
|
363
|
+
required(p.region);
|
|
364
|
+
},
|
|
365
|
+
{ submission: { action: () => Promise.resolve(undefined) } },
|
|
366
|
+
);
|
|
367
|
+
}
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
## Desktop
|
|
371
|
+
|
|
372
|
+
Hover darkens the border (`text-muted`, `danger` in the error state) with the pointer cursor; a read-only select keeps the default cursor. Hover styles apply only on devices that can hover, so a tap never leaves one stuck. The `primary` border and the focus ring show on every focus, as on the text fields. The picker is the browser's own list, so it matches the operating system. Fields default to `md` (40 px); `provideBfDefaults({ fieldSize: 'sm' })` gives 32 px selects for toolbars and filter bars ([density guide](https://bitforge-ui.web.app/density)).
|
|
373
|
+
|
|
374
|
+
## Theming
|
|
375
|
+
|
|
376
|
+
- Border: `border-strong` (the control boundary, ≥ 3:1), switching to `danger-border` on error; focus: `primary` border with a `ring` ring.
|
|
377
|
+
- Hover (pointer cursor): border `text-muted`, or `danger` in the error state.
|
|
378
|
+
- Disabled: `surface-sunken` border, `surface-muted` background, `text-disabled` text. Read-only: `surface-muted` background, default cursor. Placeholder text: `text-muted` (≥ 4.5:1). Chevron: `text-subtle`.
|
|
379
|
+
- Label, help and error text come from the `bf-field` wrapper (see its Theming section).
|
|
380
|
+
|
|
381
|
+
## Accessibility
|
|
382
|
+
|
|
383
|
+
Native `<select>`, so it opens the platform's own picker on phones and stays fully keyboard-operable. `bf-field` renders the `<label for>`; the select itself wires `aria-describedby` to the help paragraph and the error paragraph under it (both while shown, help first) and, while it is invalid, `aria-errormessage` to the error paragraph; `aria-invalid` is set once touched and invalid (an explicit `errorMessage` shows immediately). The `placeholder`, when set, is a disabled option so it can be shown but never chosen. `aria-label` and `aria-labelledby` carry `ariaLabel` and `ariaLabelledby` only while there is no visible `label`. Read-only: `aria-readonly="true"`, the select stays focusable, a mouse press focuses it without opening the picker, the picker keys (arrows, Alt+Arrow, Enter, Space, F4, type-ahead) are blocked, and a change that still gets through (the platform picker on touch screens) is reverted, so the value never changes. `focus()` focuses the native select; Signal Forms calls it from `focusBoundControl()`.
|
|
384
|
+
|
|
385
|
+
### Keyboard
|
|
386
|
+
|
|
387
|
+
The select is a native `<select>`, so these are the browser's keys and differ slightly between operating systems. While `readonly`, the picker and type-ahead keys are blocked; <kbd>Tab</kbd> and <kbd>Escape</kbd> still work.
|
|
388
|
+
|
|
389
|
+
| Key | Action |
|
|
390
|
+
| ---------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
391
|
+
| <kbd>Tab</kbd> / <kbd>Shift</kbd>+<kbd>Tab</kbd> | Moves focus to and from the select; a `readonly` one stays in the tab order |
|
|
392
|
+
| <kbd>Space</kbd> | Opens the picker |
|
|
393
|
+
| <kbd>Alt</kbd>+<kbd>ArrowDown</kbd> / <kbd>F4</kbd> | Opens the picker on Windows and Linux |
|
|
394
|
+
| <kbd>ArrowUp</kbd> / <kbd>ArrowDown</kbd> | Closed: chooses the previous / next option on Windows and Linux (<kbd>ArrowLeft</kbd> / <kbd>ArrowRight</kbd> too), opens the picker on macOS; open: moves the highlight |
|
|
395
|
+
| <kbd>Home</kbd> / <kbd>End</kbd> | Closed on Windows and Linux: chooses the first / last option; open: highlights the first / last option |
|
|
396
|
+
| <kbd>PageUp</kbd> / <kbd>PageDown</kbd> | Moves several options up / down (to the first / last in a short list) |
|
|
397
|
+
| <kbd>Enter</kbd> | In the open picker: chooses the highlighted option |
|
|
398
|
+
| <kbd>Escape</kbd> | Closes the open picker without changing the value |
|
|
399
|
+
| <kbd>A</kbd>–<kbd>Z</kbd>, <kbd>0</kbd>–<kbd>9</kbd> | Type-ahead: moves to the next option whose label starts with the typed text |
|
|
400
|
+
|
|
401
|
+
## Inputs
|
|
402
|
+
|
|
403
|
+
| Name | Type | Default | Description |
|
|
404
|
+
| ---------------- | ----------------------------------------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
405
|
+
| `options` | `readonly BfSelectOption<T>[]` | required | The options to choose from. |
|
|
406
|
+
| `compareWith` | `BfCompareWith<T>` | `Object.is` | Equality for matching `value` to an option (default `Object.is`); pass one for object values. |
|
|
407
|
+
| `size` | `BfSize \| undefined` (`'sm' \| 'md' \| 'lg' \| undefined`) | `undefined` | Height step: sm 32, md 40, lg 48 px; unset follows `provideBfDefaults()` (`fieldSize`, `md` by default). |
|
|
408
|
+
| `label` | `string` | `''` | Visible label above the control. |
|
|
409
|
+
| `ariaLabel` | `string` | `''` | Accessible name when no visible label is rendered (e.g. a table filter). Ignored when `label` is set. |
|
|
410
|
+
| `ariaLabelledby` | `string` | `''` | Id(s) of the element(s) that name the field when no visible label is rendered. Ignored when `label` is set. |
|
|
411
|
+
| `placeholder` | `string` | `''` | Disabled first option shown while nothing is selected. |
|
|
412
|
+
| `helpText` | `string` | `''` | Helper text under the control. |
|
|
413
|
+
| `errorMessage` | `string` | `''` | Explicit error text; takes precedence over Signal Forms errors. |
|
|
414
|
+
| `disabled` | `boolean` | `false` | Disables the control. Set by `[formField]`; do not bind it yourself. |
|
|
415
|
+
| `readonly` | `boolean` | `false` | Shows the value but blocks changes; the select stays focusable and exposes `aria-readonly`. Set by `[formField]`; do not bind it yourself. |
|
|
416
|
+
| `required` | `boolean` | `false` | Marks the control required. Set by `[formField]`; do not bind it yourself. |
|
|
417
|
+
| `invalid` | `boolean` | `false` | Field validity from Signal Forms. Set by `[formField]`; do not bind it yourself. |
|
|
418
|
+
| `touched` | `boolean` | `false` | Whether the user has left the field. Set by `[formField]`; do not bind it yourself. |
|
|
419
|
+
| `errors` | `readonly ValidationError.WithOptionalFieldTree[]` | `[]` | Validation errors from Signal Forms. Set by `[formField]`; do not bind it yourself. |
|
|
420
|
+
| `name` | `string` | `''` | Native name attribute. Set by `[formField]`; do not bind it yourself. |
|
|
421
|
+
| `id` | `string` | `generated` | Control id; generated when not set. |
|
|
422
|
+
| `value` | `T \| null` | `null` | The selected option's value, `null` while nothing is selected; two-way bindable and bound by `[formField]`. Two-way: `[(value)]`. |
|
|
423
|
+
|
|
424
|
+
## Outputs
|
|
425
|
+
|
|
426
|
+
| Name | Payload | Description |
|
|
427
|
+
| --------- | ------------ | ------------------------------------------------------------------------------------------------------- |
|
|
428
|
+
| `changed` | `T` | Emitted with the chosen value after a user change (never `null`). |
|
|
429
|
+
| `focused` | `FocusEvent` | Emitted when the control gains focus. |
|
|
430
|
+
| `blurred` | `FocusEvent` | Emitted when the control loses focus. |
|
|
431
|
+
| `touch` | `void` | Emitted on blur so Signal Forms marks the field touched. Set by `[formField]`; do not bind it yourself. |
|
|
432
|
+
|
|
433
|
+
## Methods
|
|
434
|
+
|
|
435
|
+
### `focus(options?: FocusOptions): void`
|
|
436
|
+
|
|
437
|
+
Focuses the native select; Signal Forms calls it from `focusBoundControl()`.
|
|
438
|
+
|
|
439
|
+
| Parameter | Type | Description |
|
|
440
|
+
| ---------- | -------------- | --------------------------------------------------------------------- |
|
|
441
|
+
| `options?` | `FocusOptions` | Passed on to the native `focus()`, such as `{ preventScroll: true }`. |
|
|
442
|
+
|
|
443
|
+
## Notes
|
|
444
|
+
|
|
445
|
+
### Do
|
|
446
|
+
|
|
447
|
+
- Provide a `placeholder` when no option is a sensible default, and make the field required.
|
|
448
|
+
- Keep option labels short; long labels are truncated by the native control.
|
|
449
|
+
|
|
450
|
+
### Don't
|
|
451
|
+
|
|
452
|
+
- Don't use it for two or three options that fit on screen; use `bf-radio-group` or `bf-segmented`.
|
|
453
|
+
|
|
454
|
+
## Migration notes
|
|
455
|
+
|
|
456
|
+
### 0.3.0
|
|
457
|
+
|
|
458
|
+
**Breaking:** typed values. `BfSelectComponent<T = string>` is generic over the option value
|
|
459
|
+
(`BfSelectOption<T = string>`), so numbers, ids and objects bind directly; `value` is `T | null`
|
|
460
|
+
and defaults to `null` (was `''`), which shows the placeholder. New `compareWith`
|
|
461
|
+
(`BfCompareWith<T>`, default `Object.is`) matches `value` to an option, and new `changed` emits the
|
|
462
|
+
chosen value (never `null`) after a user change. A string, number or boolean option value is the
|
|
463
|
+
native `<option value>` (`"42"` for `42`); an object option's is its index; a choice maps back by
|
|
464
|
+
position, so duplicate native values still resolve. An option whose value is `''` is now
|
|
465
|
+
selectable instead of reading as "empty"; an option whose value is `null` is unsupported (it reads
|
|
466
|
+
as nothing selected). See Migration 6, 7 and 9.
|
|
467
|
+
|
|
468
|
+
**Breaking:** the default `size` is `md` (40 px) instead of `lg` (48 px); `size` is a `BfSize`
|
|
469
|
+
(`sm` 32, `md` 40, `lg` 48 px — `sm` is new) and, unset, follows `provideBfDefaults()`'s
|
|
470
|
+
`fieldSize`. See Migration 2. The chevron and the text padding are logical, so the chevron sits at
|
|
471
|
+
the inline end under `dir="rtl"`.
|
|
472
|
+
|
|
473
|
+
**Behaviour:** an error without a message shows its kind's default text (was a red border only):
|
|
474
|
+
once touched, the field shows the first error's own `message`, else `provideBfLabels()`'s
|
|
475
|
+
`validation` text for its kind (Signal Forms and Reactive interop kinds), else
|
|
476
|
+
`validation.fallback`. `errorMessage` still wins. See Migration 1.
|
|
477
|
+
|
|
478
|
+
JSDoc of `select.component.ts` for the API reference: `@summary`/`@since` on the exported symbols;
|
|
479
|
+
`@param` docs; template handlers and framework hooks marked `@internal`, so the reference leaves
|
|
480
|
+
them out. No behaviour change.
|
|
481
|
+
|
|
482
|
+
**Behaviour:** the help text stays while an error shows, with the error under it (F-020):
|
|
483
|
+
`aria-describedby` lists both (`"<id>-help <id>-error"`), and `aria-errormessage` points at the
|
|
484
|
+
error paragraph while `aria-invalid="true"`. See Migration 25.
|
|
485
|
+
|
|
486
|
+
### 0.2.0
|
|
487
|
+
|
|
488
|
+
The rest border is `border-strong`; the empty value (placeholder) is `text-muted` (was
|
|
489
|
+
`text-subtle`). The chevron stays `text-subtle`.
|
|
490
|
+
|
|
491
|
+
Changed: the border narrows to 1 px (was 1.5 px); the chevron stroke stays 1.5 px.
|
|
492
|
+
|
|
493
|
+
Changed: the focus ring narrows to 2 px (was 4 px), as on `bf-input`.
|
|
494
|
+
|
|
495
|
+
Fixed: the native select shows a pointer cursor and a hover border (`text-muted`, `danger` in the
|
|
496
|
+
error state); focus and disabled borders still win.
|
|
497
|
+
|
|
498
|
+
Added: `readonly` input. Behaviour change: the control ignored Signal Forms `readonly()` rules and
|
|
499
|
+
could be edited; it now declares `readonly` and blocks changes. While read-only the select keeps its
|
|
500
|
+
value on the `surface-muted` background with `aria-readonly="true"` and stays in the tab order; a
|
|
501
|
+
mouse press focuses it without opening the picker, the picker keys (arrows, Alt+Arrow, Enter, Space,
|
|
502
|
+
F4, type-ahead) are blocked, and a change that still gets through (the platform picker on touch
|
|
503
|
+
screens, which offers no press to cancel) is reverted without writing the model. `ariaLabel` / `ariaLabelledby` name a
|
|
504
|
+
select without a visible `label` (ignored while `label` is set). `focus(options?)` focuses the
|
|
505
|
+
native control, so Signal Forms' `focusBoundControl()` and "focus the first invalid field" on submit
|
|
506
|
+
now work (they were no-ops).
|
|
507
|
+
|
|
508
|
+
### 0.1.0
|
|
509
|
+
|
|
510
|
+
Same `md`/`lg` size rename as `bf-input`. Without a `placeholder`, an unmatched `value` (e.g. the empty string) now renders a hidden, disabled, selected placeholder option so the native control shows no visible selection instead of falling back to the first real option. The inert `[value]` binding on `<select>` was removed; selection is driven entirely by `[selected]` on each `<option>`. The host no longer reflects a static `id` attribute onto `<bf-select>`. Added `bg-none` so consumers loading `@tailwindcss/forms` don't get a second, duplicate chevron drawn by the plugin's default `background-image` alongside the library's own CSS chevron.
|