ng-hub-ui-forms 21.0.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/README.md +252 -0
- package/fesm2022/ng-hub-ui-forms-signals.mjs +93 -0
- package/fesm2022/ng-hub-ui-forms-signals.mjs.map +1 -0
- package/fesm2022/ng-hub-ui-forms.mjs +5882 -0
- package/fesm2022/ng-hub-ui-forms.mjs.map +1 -0
- package/ng-hub-ui-forms-21.0.0.tgz +0 -0
- package/package.json +58 -0
- package/signals/README.md +63 -0
- package/src/lib/styles/_field.scss +132 -0
- package/src/lib/styles/_tokens.scss +179 -0
- package/src/lib/styles/index.scss +13 -0
- package/types/ng-hub-ui-forms-signals.d.ts +65 -0
- package/types/ng-hub-ui-forms.d.ts +1463 -0
package/README.md
ADDED
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
# ng-hub-ui-forms
|
|
2
|
+
|
|
3
|
+
[Español](./README.es.md) | **English**
|
|
4
|
+
|
|
5
|
+
Accessible, **signal-based form fields** for Angular — input, textarea, slider,
|
|
6
|
+
select and datepicker — with **automatic validation-error display** for controls,
|
|
7
|
+
`FormGroup`s and `FormArray`s. Reactive Forms today, Signal Forms ready. Themed
|
|
8
|
+
entirely through `--hub-*` CSS variables, no Bootstrap required.
|
|
9
|
+
|
|
10
|
+
## Documentation and Live Examples
|
|
11
|
+
|
|
12
|
+
This package is part of [Hub UI](https://hubui.dev/), a collection of Angular component libraries for standalone apps.
|
|
13
|
+
|
|
14
|
+
- Docs: https://hubui.dev/forms/overview/
|
|
15
|
+
- Live examples: https://hubui.dev/forms/examples/
|
|
16
|
+
- Hub UI: https://hubui.dev/
|
|
17
|
+
|
|
18
|
+
## 🧩 Library Family `ng-hub-ui`
|
|
19
|
+
|
|
20
|
+
This library is part of the **ng-hub-ui** ecosystem:
|
|
21
|
+
|
|
22
|
+
- [**ng-hub-ui-accordion**](https://www.npmjs.com/package/ng-hub-ui-accordion) _(deprecated → use panels)_
|
|
23
|
+
- [**ng-hub-ui-action-sheet**](https://www.npmjs.com/package/ng-hub-ui-action-sheet)
|
|
24
|
+
- [**ng-hub-ui-avatar**](https://www.npmjs.com/package/ng-hub-ui-avatar)
|
|
25
|
+
- [**ng-hub-ui-board**](https://www.npmjs.com/package/ng-hub-ui-board)
|
|
26
|
+
- [**ng-hub-ui-breadcrumbs**](https://www.npmjs.com/package/ng-hub-ui-breadcrumbs)
|
|
27
|
+
- [**ng-hub-ui-calendar**](https://www.npmjs.com/package/ng-hub-ui-calendar)
|
|
28
|
+
- [**ng-hub-ui-dropdown**](https://www.npmjs.com/package/ng-hub-ui-dropdown)
|
|
29
|
+
- [**ng-hub-ui-forms**](https://www.npmjs.com/package/ng-hub-ui-forms) ← You are here
|
|
30
|
+
- [**ng-hub-ui-history**](https://www.npmjs.com/package/ng-hub-ui-history)
|
|
31
|
+
- [**ng-hub-ui-modal**](https://www.npmjs.com/package/ng-hub-ui-modal)
|
|
32
|
+
- [**ng-hub-ui-nav**](https://www.npmjs.com/package/ng-hub-ui-nav)
|
|
33
|
+
- [**ng-hub-ui-paginable**](https://www.npmjs.com/package/ng-hub-ui-paginable)
|
|
34
|
+
- [**ng-hub-ui-panels**](https://www.npmjs.com/package/ng-hub-ui-panels)
|
|
35
|
+
- [**ng-hub-ui-portal**](https://www.npmjs.com/package/ng-hub-ui-portal)
|
|
36
|
+
- [**ng-hub-ui-sortable**](https://www.npmjs.com/package/ng-hub-ui-sortable)
|
|
37
|
+
- [**ng-hub-ui-stepper**](https://www.npmjs.com/package/ng-hub-ui-stepper)
|
|
38
|
+
- [**ng-hub-ui-utils**](https://www.npmjs.com/package/ng-hub-ui-utils)
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## 🚀 Quick Start
|
|
43
|
+
|
|
44
|
+
### 1. Install
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npm install ng-hub-ui-forms
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
`@angular/cdk` is a peer dependency (used by the datepicker overlay and the select):
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
npm install @angular/cdk
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### 2. Import
|
|
57
|
+
|
|
58
|
+
The fields are standalone — import only what you use:
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
import { HubInputComponent, HubSelectComponent } from 'ng-hub-ui-forms';
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### 3. Use
|
|
65
|
+
|
|
66
|
+
```html
|
|
67
|
+
<form [formGroup]="form" hubForm (submit)="save()">
|
|
68
|
+
<hub-input formControlName="email" type="email" label="Email" required />
|
|
69
|
+
<hub-select formControlName="country" label="Country" [items]="countries" bindLabel="name" bindValue="code" />
|
|
70
|
+
<button type="submit">Save</button>
|
|
71
|
+
</form>
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
The required `email` field reveals its error automatically on submit — no manual
|
|
75
|
+
`@if (control.invalid && control.touched)` wiring.
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## 📦 Description
|
|
80
|
+
|
|
81
|
+
`ng-hub-ui-forms` unifies a set of accessible form fields behind one contract:
|
|
82
|
+
bind them with **Reactive Forms** and the matching validation errors appear
|
|
83
|
+
**automatically** at the control, group and form level. Fields are standalone,
|
|
84
|
+
`OnPush` and signal-native; the select is a maintained fork of ng-select; the
|
|
85
|
+
datepicker is built from scratch on native `Date` and the Angular CDK overlay.
|
|
86
|
+
Everything is themed through canonical `--hub-*` CSS variables with runtime dark
|
|
87
|
+
mode — no Bootstrap dependency.
|
|
88
|
+
|
|
89
|
+
## 🎯 Features
|
|
90
|
+
|
|
91
|
+
- **Fields** — `hub-input` (text/number/email/password/color/switch/checkbox/counter, with input-group addons & masks), `hub-otp`, `hub-textarea` (+ `hubAutoresize`), `hub-slider`, `hub-select` (dropdown / buttons / checkbox / radio formats, grouping, typeahead, custom templates), `hub-datepicker` (single & range, keyboard nav, i18n).
|
|
92
|
+
- **Automatic error display** — bind a field and its control errors render below it; `hub-fieldset`, `form[hubForm]` and `hub-legend` surface group- and form-level (cross-field) errors the same way, with zero wiring.
|
|
93
|
+
- **Containers** — `hub-fieldset` / `form[hubForm]` group fields and show their group errors; `hub-legend` renders an accessible legend.
|
|
94
|
+
- **Configurable** — `provideHubForms({ … })` sets the invalid-feedback templates, datepicker locale/labels and more, app-wide or per instance.
|
|
95
|
+
- **Validators & helpers** — `hubAreEqual` cross-field validator, `hubValidationError` / `hubFormText` projection directives, and a set of utility pipes.
|
|
96
|
+
- **Signal Forms ready** — an opt-in [`ng-hub-ui-forms/signals`](#-signal-forms-opt-in) secondary entry point integrates Angular Signal Forms; the core stays Reactive-Forms-based and Angular-21-safe.
|
|
97
|
+
- **Theming** — every colour, border, radius and spacing is a `--hub-*` CSS custom property; ships shared SCSS tokens for consumers.
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## 📦 Installation
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
npm install ng-hub-ui-forms @angular/cdk
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Peer Dependencies
|
|
108
|
+
|
|
109
|
+
```json
|
|
110
|
+
{
|
|
111
|
+
"@angular/cdk": ">=21.0.0",
|
|
112
|
+
"@angular/common": ">=21.0.0",
|
|
113
|
+
"@angular/core": ">=21.0.0",
|
|
114
|
+
"@angular/forms": ">=21.0.0",
|
|
115
|
+
"@angular/platform-browser": ">=21.0.0"
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## ⚙️ Usage
|
|
122
|
+
|
|
123
|
+
### Input
|
|
124
|
+
|
|
125
|
+
```html
|
|
126
|
+
<hub-input formControlName="email" type="email" label="Email" required />
|
|
127
|
+
<hub-input formControlName="amount" type="number" label="Amount" />
|
|
128
|
+
<hub-input formControlName="darkMode" format="switch" label="Dark mode" />
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### Select
|
|
132
|
+
|
|
133
|
+
```html
|
|
134
|
+
<!-- object items -->
|
|
135
|
+
<hub-select formControlName="country" label="Country" [items]="countries" bindLabel="name" bindValue="code" />
|
|
136
|
+
|
|
137
|
+
<!-- multiple + typeahead -->
|
|
138
|
+
<hub-select formControlName="tags" label="Tags" [items]="tags" [multiple]="true" [searchable]="true" />
|
|
139
|
+
|
|
140
|
+
<!-- grouped -->
|
|
141
|
+
<hub-select formControlName="city" label="City" [items]="cities" bindLabel="name" bindValue="id" groupBy="country" />
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Custom option/label templates are projected straight through to the engine:
|
|
145
|
+
|
|
146
|
+
```html
|
|
147
|
+
<hub-select formControlName="assignee" [items]="people" bindLabel="name">
|
|
148
|
+
<ng-template ng-label-tmp let-item="item">{{ item.emoji }} {{ item.name }}</ng-template>
|
|
149
|
+
<ng-template ng-option-tmp let-item="item"><strong>{{ item.name }}</strong> — {{ item.role }}</ng-template>
|
|
150
|
+
</hub-select>
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
> Import `NgOptionTemplateDirective` / `NgLabelTemplateDirective` from `ng-hub-ui-forms`.
|
|
154
|
+
> The dropdown panel renders to `body` by default (`appendTo`) so it is never clipped by cards or scroll containers.
|
|
155
|
+
|
|
156
|
+
### Datepicker
|
|
157
|
+
|
|
158
|
+
```html
|
|
159
|
+
<hub-datepicker formControlName="date" label="Date" />
|
|
160
|
+
<hub-datepicker formControlName="range" mode="range" label="Stay" />
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Automatic errors at every level
|
|
164
|
+
|
|
165
|
+
```html
|
|
166
|
+
<form [formGroup]="form" hubForm (submit)="save()">
|
|
167
|
+
<hub-fieldset legend="Credentials">
|
|
168
|
+
<hub-input formControlName="email" type="email" label="Email" required />
|
|
169
|
+
<hub-input formControlName="confirm" type="email" label="Confirm email" required />
|
|
170
|
+
</hub-fieldset>
|
|
171
|
+
<button type="submit">Create account</button>
|
|
172
|
+
</form>
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
```ts
|
|
176
|
+
form = new FormGroup(
|
|
177
|
+
{ email: new FormControl('', Validators.required), confirm: new FormControl('') },
|
|
178
|
+
{ validators: hubAreEqual('email', 'confirm') }
|
|
179
|
+
);
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
On submit, each invalid field shows its error and the cross-field `hubAreEqual`
|
|
183
|
+
error is surfaced by the fieldset/form — no manual error markup anywhere.
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## 🛠️ Configuration
|
|
188
|
+
|
|
189
|
+
Provide app-wide defaults (invalid-feedback copy, datepicker locale/labels…):
|
|
190
|
+
|
|
191
|
+
```ts
|
|
192
|
+
import { provideHubForms } from 'ng-hub-ui-forms';
|
|
193
|
+
|
|
194
|
+
bootstrapApplication(AppComponent, {
|
|
195
|
+
providers: [
|
|
196
|
+
provideHubForms({
|
|
197
|
+
datepicker: { firstDayOfWeek: 1, displayFormat: 'dd/MM/yyyy' }
|
|
198
|
+
})
|
|
199
|
+
]
|
|
200
|
+
});
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## 🎨 Styling
|
|
206
|
+
|
|
207
|
+
Everything is themed through `--hub-*` CSS custom properties. The package ships
|
|
208
|
+
shared SCSS tokens; import them once at the app root:
|
|
209
|
+
|
|
210
|
+
```scss
|
|
211
|
+
@use 'ng-hub-ui-forms/src/lib/styles/index' as hub-forms;
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
```css
|
|
215
|
+
hub-input,
|
|
216
|
+
hub-select {
|
|
217
|
+
--hub-field-border-color: #cbd5e1;
|
|
218
|
+
--hub-select-option-selected-bg: #e0e7ff;
|
|
219
|
+
}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
## ✨ Signal Forms (opt-in)
|
|
225
|
+
|
|
226
|
+
`ng-hub-ui-forms/signals` is a secondary entry point — the only place that
|
|
227
|
+
imports `@angular/forms/signals`, so the core stays Angular-21-safe. Recommended
|
|
228
|
+
on Angular ≥ 22.
|
|
229
|
+
|
|
230
|
+
```ts
|
|
231
|
+
import { HubSignalFieldControl, hubSignalErrorMessages } from 'ng-hub-ui-forms/signals';
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
---
|
|
235
|
+
|
|
236
|
+
## ♿ Accessibility
|
|
237
|
+
|
|
238
|
+
- Labels are associated with their control (`for`/`id`); required fields are marked.
|
|
239
|
+
- Validation errors render in an `role="alert"` region tied to the field.
|
|
240
|
+
- The select exposes correct combobox/listbox semantics; the datepicker is fully keyboard-navigable.
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
## 📊 Changelog
|
|
245
|
+
|
|
246
|
+
See [CHANGELOG.md](./CHANGELOG.md).
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
## 📄 License
|
|
251
|
+
|
|
252
|
+
MIT © [Carlos Morcillo](https://www.carlosmorcillo.com)
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { inject, model, input, computed, Directive } from '@angular/core';
|
|
3
|
+
import { defaultInvalidFeedback, HUB_FORMS_CONFIG } from 'ng-hub-ui-forms';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Signal Forms adapter for the shared Hub error-display logic.
|
|
7
|
+
*
|
|
8
|
+
* It mirrors the core `HubErrorDisplay` (CVA/Reactive) behaviour for the Signal
|
|
9
|
+
* Forms model: given the validation errors a field exposes through `field().errors()`,
|
|
10
|
+
* it produces the same human-readable messages. The single source of truth for the
|
|
11
|
+
* default copy stays in the core `defaultInvalidFeedback` (reused here), so Reactive
|
|
12
|
+
* and Signal fields render identical messages.
|
|
13
|
+
*
|
|
14
|
+
* Resolution order per error:
|
|
15
|
+
* 1. The error's own `message` (Signal Forms validators may attach one).
|
|
16
|
+
* 2. The provided `fn` override (typically `HUB_FORMS_CONFIG.invalidFeedbackTemplateFn`
|
|
17
|
+
* or a per-field override), called with the error `kind` and the error object.
|
|
18
|
+
*
|
|
19
|
+
* @param errors Validation errors read from a Signal Forms field (`field().errors()`).
|
|
20
|
+
* @param fn Message factory used when an error carries no inline `message`. Defaults
|
|
21
|
+
* to the core `defaultInvalidFeedback`.
|
|
22
|
+
* @returns The resolved, human-readable error messages in error order.
|
|
23
|
+
*/
|
|
24
|
+
function hubSignalErrorMessages(errors, fn = defaultInvalidFeedback) {
|
|
25
|
+
return (errors ?? []).map((error) => error.message ?? fn(error.kind, error));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Abstract base for Hub form fields that integrate with Angular **Signal Forms**
|
|
30
|
+
* via the `Field` directive (`[formField]`).
|
|
31
|
+
*
|
|
32
|
+
* It implements the {@link FormValueControl} contract — exposing a two-way `value`
|
|
33
|
+
* model the `Field` directive keeps in sync with the bound `FieldTree`, plus the
|
|
34
|
+
* optional state inputs (`errors`, `disabled`, `touched`, `required`) the directive
|
|
35
|
+
* auto-wires — and resolves error messages through the same `HubErrorDisplay`
|
|
36
|
+
* pipeline used by the CVA core (so Reactive and Signal fields look identical).
|
|
37
|
+
*
|
|
38
|
+
* Decorated with `@Directive()` so subclasses (`@Component`) inherit its signal
|
|
39
|
+
* inputs/model, mirroring the core `HubFieldControl` pattern. It lives in the opt-in
|
|
40
|
+
* `ng-hub-ui-forms/signals` entry point and is the only place that depends on
|
|
41
|
+
* `@angular/forms/signals`; the core package never imports it.
|
|
42
|
+
*
|
|
43
|
+
* @template TValue The value type the field edits.
|
|
44
|
+
*/
|
|
45
|
+
class HubSignalFieldControl {
|
|
46
|
+
/** Hub forms configuration providing the default error-message factory. */
|
|
47
|
+
hubFormsConfig = inject(HUB_FORMS_CONFIG);
|
|
48
|
+
/** Two-way model the `Field` directive keeps in sync with the bound `FieldTree` value. */
|
|
49
|
+
value = model.required(/* @ts-ignore */
|
|
50
|
+
...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
51
|
+
/** Validation errors bound from the field by the `Field` directive. */
|
|
52
|
+
errors = input([], /* @ts-ignore */
|
|
53
|
+
...(ngDevMode ? [{ debugName: "errors" }] : /* istanbul ignore next */ []));
|
|
54
|
+
/** Disabled state bound from the field by the `Field` directive. */
|
|
55
|
+
disabled = input(false, /* @ts-ignore */
|
|
56
|
+
...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
|
|
57
|
+
/** Touched state bound from the field by the `Field` directive. */
|
|
58
|
+
touched = input(false, /* @ts-ignore */
|
|
59
|
+
...(ngDevMode ? [{ debugName: "touched" }] : /* istanbul ignore next */ []));
|
|
60
|
+
/** Whether the field is required, bound from the field by the `Field` directive. */
|
|
61
|
+
required = input(false, /* @ts-ignore */
|
|
62
|
+
...(ngDevMode ? [{ debugName: "required" }] : /* istanbul ignore next */ []));
|
|
63
|
+
/** Human-readable messages for the current `errors()`, via the shared Hub error display. */
|
|
64
|
+
messages = computed(() => hubSignalErrorMessages(this.errors(), this.hubFormsConfig.invalidFeedbackTemplateFn), /* @ts-ignore */
|
|
65
|
+
...(ngDevMode ? [{ debugName: "messages" }] : /* istanbul ignore next */ []));
|
|
66
|
+
/** Whether the field should render its invalid state (touched and holding errors). */
|
|
67
|
+
isInvalid = computed(() => this.touched() && this.errors().length > 0, /* @ts-ignore */
|
|
68
|
+
...(ngDevMode ? [{ debugName: "isInvalid" }] : /* istanbul ignore next */ []));
|
|
69
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: HubSignalFieldControl, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
70
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.1", type: HubSignalFieldControl, isStandalone: true, inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null }, errors: { classPropertyName: "errors", publicName: "errors", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, touched: { classPropertyName: "touched", publicName: "touched", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, ngImport: i0 });
|
|
71
|
+
}
|
|
72
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.1", ngImport: i0, type: HubSignalFieldControl, decorators: [{
|
|
73
|
+
type: Directive
|
|
74
|
+
}], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: true }] }, { type: i0.Output, args: ["valueChange"] }], errors: [{ type: i0.Input, args: [{ isSignal: true, alias: "errors", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], touched: [{ type: i0.Input, args: [{ isSignal: true, alias: "touched", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }] } });
|
|
75
|
+
|
|
76
|
+
/*
|
|
77
|
+
* Public API Surface of ng-hub-ui-forms/signals
|
|
78
|
+
*
|
|
79
|
+
* Opt-in Angular **Signal Forms** (`@angular/forms/signals`) integration for the
|
|
80
|
+
* Hub forms library. Importing this entry point is the ONLY place that pulls in the
|
|
81
|
+
* Signal Forms API; the core `ng-hub-ui-forms` package never imports it, so the core
|
|
82
|
+
* stays compatible with Angular 21 (where Signal Forms is experimental).
|
|
83
|
+
*
|
|
84
|
+
* Recommended on Angular >= 22, where Signal Forms is stable.
|
|
85
|
+
*/
|
|
86
|
+
// Signal-model base for fields bound via the `Field` directive (`[formField]`).
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Generated bundle index. Do not edit.
|
|
90
|
+
*/
|
|
91
|
+
|
|
92
|
+
export { HubSignalFieldControl, hubSignalErrorMessages };
|
|
93
|
+
//# sourceMappingURL=ng-hub-ui-forms-signals.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ng-hub-ui-forms-signals.mjs","sources":["../../../projects/forms/signals/src/lib/signal-error-display.ts","../../../projects/forms/signals/src/lib/hub-signal-field-control.ts","../../../projects/forms/signals/src/public-api.ts","../../../projects/forms/signals/src/ng-hub-ui-forms-signals.ts"],"sourcesContent":["import type { ValidationError } from '@angular/forms/signals';\nimport { defaultInvalidFeedback } from 'ng-hub-ui-forms';\n\n/**\n * Signal Forms adapter for the shared Hub error-display logic.\n *\n * It mirrors the core `HubErrorDisplay` (CVA/Reactive) behaviour for the Signal\n * Forms model: given the validation errors a field exposes through `field().errors()`,\n * it produces the same human-readable messages. The single source of truth for the\n * default copy stays in the core `defaultInvalidFeedback` (reused here), so Reactive\n * and Signal fields render identical messages.\n *\n * Resolution order per error:\n * 1. The error's own `message` (Signal Forms validators may attach one).\n * 2. The provided `fn` override (typically `HUB_FORMS_CONFIG.invalidFeedbackTemplateFn`\n * or a per-field override), called with the error `kind` and the error object.\n *\n * @param errors Validation errors read from a Signal Forms field (`field().errors()`).\n * @param fn Message factory used when an error carries no inline `message`. Defaults\n * to the core `defaultInvalidFeedback`.\n * @returns The resolved, human-readable error messages in error order.\n */\nexport function hubSignalErrorMessages(\n\terrors: readonly ValidationError[] | null | undefined,\n\tfn: (key: string, value: unknown) => string = defaultInvalidFeedback\n): string[] {\n\treturn (errors ?? []).map((error) => error.message ?? fn(error.kind, error));\n}\n","import { Directive, computed, inject, input, model, type ModelSignal, type Signal } from '@angular/core';\nimport type { FormValueControl, ValidationError } from '@angular/forms/signals';\nimport { HUB_FORMS_CONFIG } from 'ng-hub-ui-forms';\nimport { hubSignalErrorMessages } from './signal-error-display';\n\n/**\n * Abstract base for Hub form fields that integrate with Angular **Signal Forms**\n * via the `Field` directive (`[formField]`).\n *\n * It implements the {@link FormValueControl} contract — exposing a two-way `value`\n * model the `Field` directive keeps in sync with the bound `FieldTree`, plus the\n * optional state inputs (`errors`, `disabled`, `touched`, `required`) the directive\n * auto-wires — and resolves error messages through the same `HubErrorDisplay`\n * pipeline used by the CVA core (so Reactive and Signal fields look identical).\n *\n * Decorated with `@Directive()` so subclasses (`@Component`) inherit its signal\n * inputs/model, mirroring the core `HubFieldControl` pattern. It lives in the opt-in\n * `ng-hub-ui-forms/signals` entry point and is the only place that depends on\n * `@angular/forms/signals`; the core package never imports it.\n *\n * @template TValue The value type the field edits.\n */\n@Directive()\nexport abstract class HubSignalFieldControl<TValue> implements FormValueControl<TValue> {\n\t/** Hub forms configuration providing the default error-message factory. */\n\tprotected readonly hubFormsConfig = inject(HUB_FORMS_CONFIG);\n\n\t/** Two-way model the `Field` directive keeps in sync with the bound `FieldTree` value. */\n\treadonly value: ModelSignal<TValue> = model.required<TValue>();\n\n\t/** Validation errors bound from the field by the `Field` directive. */\n\treadonly errors = input<readonly ValidationError.WithOptionalFieldTree[]>([]);\n\n\t/** Disabled state bound from the field by the `Field` directive. */\n\treadonly disabled = input(false);\n\n\t/** Touched state bound from the field by the `Field` directive. */\n\treadonly touched = input(false);\n\n\t/** Whether the field is required, bound from the field by the `Field` directive. */\n\treadonly required = input(false);\n\n\t/** Human-readable messages for the current `errors()`, via the shared Hub error display. */\n\treadonly messages: Signal<string[]> = computed(() =>\n\t\thubSignalErrorMessages(this.errors(), this.hubFormsConfig.invalidFeedbackTemplateFn)\n\t);\n\n\t/** Whether the field should render its invalid state (touched and holding errors). */\n\treadonly isInvalid: Signal<boolean> = computed(() => this.touched() && this.errors().length > 0);\n}\n","/*\n * Public API Surface of ng-hub-ui-forms/signals\n *\n * Opt-in Angular **Signal Forms** (`@angular/forms/signals`) integration for the\n * Hub forms library. Importing this entry point is the ONLY place that pulls in the\n * Signal Forms API; the core `ng-hub-ui-forms` package never imports it, so the core\n * stays compatible with Angular 21 (where Signal Forms is experimental).\n *\n * Recommended on Angular >= 22, where Signal Forms is stable.\n */\n\n// Signal-model base for fields bound via the `Field` directive (`[formField]`).\nexport { HubSignalFieldControl } from './lib/hub-signal-field-control';\n\n// Signal adapter of the shared Hub error-display logic (reuses the core copy).\nexport { hubSignalErrorMessages } from './lib/signal-error-display';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":";;;;AAGA;;;;;;;;;;;;;;;;;;AAkBG;SACa,sBAAsB,CACrC,MAAqD,EACrD,KAA8C,sBAAsB,EAAA;IAEpE,OAAO,CAAC,MAAM,IAAI,EAAE,EAAE,GAAG,CAAC,CAAC,KAAK,KAAK,KAAK,CAAC,OAAO,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;AAC7E;;ACtBA;;;;;;;;;;;;;;;;AAgBG;MAEmB,qBAAqB,CAAA;;AAEvB,IAAA,cAAc,GAAG,MAAM,CAAC,gBAAgB,CAAC;;IAGnD,KAAK,GAAwB,KAAK,CAAC,QAAQ;8EAAU;;IAGrD,MAAM,GAAG,KAAK,CAAmD,EAAE;+EAAC;;IAGpE,QAAQ,GAAG,KAAK,CAAC,KAAK;iFAAC;;IAGvB,OAAO,GAAG,KAAK,CAAC,KAAK;gFAAC;;IAGtB,QAAQ,GAAG,KAAK,CAAC,KAAK;iFAAC;;AAGvB,IAAA,QAAQ,GAAqB,QAAQ,CAAC,MAC9C,sBAAsB,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,IAAI,CAAC,cAAc,CAAC,yBAAyB,CAAC;iFACpF;;AAGQ,IAAA,SAAS,GAAoB,QAAQ,CAAC,MAAM,IAAI,CAAC,OAAO,EAAE,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC,MAAM,GAAG,CAAC;kFAAC;uGAzB3E,qBAAqB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;2FAArB,qBAAqB,EAAA,YAAA,EAAA,IAAA,EAAA,MAAA,EAAA,EAAA,KAAA,EAAA,EAAA,iBAAA,EAAA,OAAA,EAAA,UAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,MAAA,EAAA,EAAA,iBAAA,EAAA,QAAA,EAAA,UAAA,EAAA,QAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,QAAA,EAAA,EAAA,iBAAA,EAAA,UAAA,EAAA,UAAA,EAAA,UAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,OAAA,EAAA,EAAA,iBAAA,EAAA,SAAA,EAAA,UAAA,EAAA,SAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,QAAA,EAAA,EAAA,iBAAA,EAAA,UAAA,EAAA,UAAA,EAAA,UAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,OAAA,EAAA,EAAA,KAAA,EAAA,aAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,CAAA;;2FAArB,qBAAqB,EAAA,UAAA,EAAA,CAAA;kBAD1C;;;ACtBD;;;;;;;;;AASG;AAEH;;ACXA;;AAEG;;;;"}
|