@zhunam/form-builder 2.0.0 → 3.1.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 +169 -4
- package/fesm2022/zhunam-form-builder.mjs +265 -33
- package/fesm2022/zhunam-form-builder.mjs.map +1 -1
- package/package.json +1 -1
- package/types/zhunam-form-builder.d.ts +266 -5
package/README.md
CHANGED
|
@@ -36,6 +36,95 @@ For a calculator-style form (no submit action, values used as you type):
|
|
|
36
36
|
|
|
37
37
|
`formSubmit` stays available in `'live'` mode too; most `'live'` consumers just won't use it.
|
|
38
38
|
|
|
39
|
+
### Presetting or replacing the form's value
|
|
40
|
+
|
|
41
|
+
The `value` input patches the form in place, without rebuilding it, so
|
|
42
|
+
whatever the user already typed in fields that stay unaffected is never
|
|
43
|
+
lost. Useful for a "swap" action or restoring a draft:
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
swapValue = signal<Partial<User> | undefined>(undefined);
|
|
47
|
+
|
|
48
|
+
onSwap(): void {
|
|
49
|
+
this.swapValue.set({ name: this.currentLastValue.name /* ... */ });
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
```html
|
|
54
|
+
<lib-form-builder [fields]="fields" [value]="swapValue()" mode="live" (valueChange)="onValueChange($event)" />
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
Rebuilding `fields()` itself (e.g. a wizard changing steps) also keeps
|
|
58
|
+
the current value of every control whose key still exists in the new
|
|
59
|
+
array; only a control whose key disappears is dropped, and only a
|
|
60
|
+
brand-new key falls back to its own `defaultValue`.
|
|
61
|
+
|
|
62
|
+
### Submitting from outside the component
|
|
63
|
+
|
|
64
|
+
```html
|
|
65
|
+
<lib-form-builder #form [fields]="fields" [hideSubmit]="true" [loading]="saving()" (formSubmit)="onSave($event)" />
|
|
66
|
+
<button (click)="form.submit()">Continue</button>
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
`submit()` runs the exact same logic the internal button would: it marks
|
|
70
|
+
the form submitted, validates, and emits `formSubmit` if valid. It's a
|
|
71
|
+
no-op while `loading` is `true`, the same guard that disables the
|
|
72
|
+
internal button.
|
|
73
|
+
|
|
74
|
+
### Switch and segmented appearance
|
|
75
|
+
|
|
76
|
+
`appearance` changes only how a specific `type` looks, never the control underneath it: a real `<input type="checkbox">`/`<input type="radio">` is always there, visually hidden but focusable, clickable via its label, and fully native for validation, keyboard operation, and screen readers.
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
fields: FieldConfig<Settings>[] = [
|
|
80
|
+
{ key: 'notifications', label: 'Email notifications', type: 'checkbox', appearance: 'switch' },
|
|
81
|
+
{
|
|
82
|
+
key: 'plan',
|
|
83
|
+
label: 'Plan',
|
|
84
|
+
type: 'radio',
|
|
85
|
+
appearance: 'segmented',
|
|
86
|
+
options: [
|
|
87
|
+
{ value: 'personal', label: 'Personal' },
|
|
88
|
+
{ value: 'business', label: 'Business' },
|
|
89
|
+
],
|
|
90
|
+
},
|
|
91
|
+
];
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
`'switch'` only changes `type: 'checkbox'`; `'segmented'` only changes `type: 'radio'`. Setting `appearance` on any other `type` (or leaving it unset) has no effect at all, no error, no warning.
|
|
95
|
+
|
|
96
|
+
### Field hints and option descriptions
|
|
97
|
+
|
|
98
|
+
`hint` shows short helper text below a field, only while there's no active error: an error replaces it visually, and `aria-describedby` follows the same rule, always pointing at whichever one is currently shown, never both.
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
{ key: 'password', label: 'Password', type: 'password', hint: 'At least 8 characters', validators: { required: true, minLength: 8 } }
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
`FieldOption.description` shows helper text below one specific `radio` option, independent of the field-level hint or error:
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
{
|
|
108
|
+
key: 'plan',
|
|
109
|
+
label: 'Plan',
|
|
110
|
+
type: 'radio',
|
|
111
|
+
options: [
|
|
112
|
+
{ value: 'personal', label: 'Personal', description: 'For individual use' },
|
|
113
|
+
{ value: 'business', label: 'Business', description: 'For teams and invoicing' },
|
|
114
|
+
],
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
### Read-only fields
|
|
119
|
+
|
|
120
|
+
`readonly` keeps a field's value visible and its validation running exactly as before, only direct editing is blocked. Unlike `disabled`, the underlying `FormControl` stays enabled, so its value is never excluded from anything.
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
{ key: 'referenceCode', label: 'Reference code', type: 'text', readonly: true, defaultValue: 'REF-2026-001' }
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Only applies to `text`, `number`, `email`, `password`, `date`, and `textarea`: the HTML `readonly` attribute doesn't apply to `select`, `radio`, or `checkbox` natively, and this library doesn't simulate it for those, setting `readonly` on one of them is simply ignored.
|
|
127
|
+
|
|
39
128
|
## API
|
|
40
129
|
|
|
41
130
|
### `FormBuilder<T>`
|
|
@@ -49,6 +138,11 @@ For a calculator-style form (no submit action, values used as you type):
|
|
|
49
138
|
| `formSubmit` | `output<T>` | N/A | Emitted with the typed form values, only when the native form and every `crossFieldValidators` check pass. |
|
|
50
139
|
| `mode` | `input<'submit' \| 'live'>` | `'submit'` | `'submit'`: only `formSubmit` fires, on submit. `'live'`: `valueChange` also fires continuously as the user edits, in addition to `formSubmit` staying available. |
|
|
51
140
|
| `valueChange` | `output<T>` | N/A | Emitted with the typed, valid values on every change, only when `mode` is `'live'`. Fires once immediately if the form starts valid with its defaults. |
|
|
141
|
+
| `submitLabel` | `input<string>` | None | Custom label for the submit button, instead of `messages.submit()`. |
|
|
142
|
+
| `hideSubmit` | `input<boolean>` | `false` | Hides the internal submit button; use the public `submit()` method to trigger submission from your own UI. |
|
|
143
|
+
| `loading` | `input<boolean>` | `false` | Disables the internal button and makes `submit()` a no-op while `true`. |
|
|
144
|
+
| `value` | `input<T>` | None | External value patched into the current form (no rebuild, no `valueChange` in `'live'` mode) every time it receives a new, non-`undefined` value. |
|
|
145
|
+
| `submit()` | `(): void` | N/A | Public method: runs the same submit logic as the internal button, works whether `hideSubmit` is `true` or `false`. |
|
|
52
146
|
|
|
53
147
|
### `FieldConfig<T>`
|
|
54
148
|
|
|
@@ -63,23 +157,57 @@ For a calculator-style form (no submit action, values used as you type):
|
|
|
63
157
|
| `defaultValue`| `T[keyof T]` | None | Initial value assigned to the field before user interaction. |
|
|
64
158
|
| `colSpan` | `1 \| 2` | `1` | How many grid columns this field spans, when the component's `columns` input is 2 or more. |
|
|
65
159
|
| `disabled` | `boolean` | `false` | Renders the control disabled from the start; still included in the value `formSubmit` emits. |
|
|
160
|
+
| `showPasswordToggle` | `boolean` | `true` | For a `type: 'password'` field, whether it renders a show/hide toggle button. No effect on other field types. |
|
|
161
|
+
| `appearance` | `FieldAppearance` (`'default' \| 'switch' \| 'segmented'`) | `'default'` | Purely visual variant. `'switch'` only affects `type: 'checkbox'`, `'segmented'` only affects `type: 'radio'`. Any other combination is ignored, same as `'default'`. See "Switch and segmented appearance" below. |
|
|
162
|
+
| `hint` | `string` | None | Short helper text shown below the field when there's no active error. See "Field hints and option descriptions" below. |
|
|
163
|
+
| `readonly` | `boolean` | `false` | Native HTML `readonly`, for `text`/`number`/`email`/`password`/`date`/`textarea` only. No effect on `select`/`radio`/`checkbox`. See "Read-only fields" below. |
|
|
164
|
+
|
|
165
|
+
`FieldOption` also gains an optional `description?: string`, shown below that specific option (for `radio`, see below); `select` has nowhere to render it, so it's ignored there.
|
|
166
|
+
|
|
167
|
+
### `FormBuilderMessages`
|
|
168
|
+
|
|
169
|
+
| Member | Type | Description |
|
|
170
|
+
| ------ | ---- | ------------ |
|
|
171
|
+
| `required()` | `() => string` | Shown when a required field is empty. |
|
|
172
|
+
| `email()` | `() => string` | Shown when an `email` field isn't well-formed. |
|
|
173
|
+
| `min(min)` | `(min: number) => string` | Shown when a numeric field is below `min`. |
|
|
174
|
+
| `max(max)` | `(max: number) => string` | Shown when a numeric field is above `max`. |
|
|
175
|
+
| `minLength(requiredLength)` | `(requiredLength: number) => string` | Shown when a field's value is shorter than `minLength`. |
|
|
176
|
+
| `maxLength(requiredLength)` | `(requiredLength: number) => string` | Shown when a field's value is longer than `maxLength`. |
|
|
177
|
+
| `pattern()` | `() => string` | Shown when a field's value doesn't match `pattern`. |
|
|
178
|
+
| `submit()` | `() => string` | Label of the submit button. |
|
|
179
|
+
| `showPassword()` | `() => string` | `aria-label` of the password toggle when the value is currently hidden. |
|
|
180
|
+
| `hidePassword()` | `() => string` | `aria-label` of the password toggle when the value is currently shown. |
|
|
181
|
+
|
|
182
|
+
| Export | Type | Description |
|
|
183
|
+
| ------ | ---- | ------------ |
|
|
184
|
+
| `FORM_BUILDER_MESSAGES` | `InjectionToken<FormBuilderMessages>` | Defaults to `FORM_BUILDER_MESSAGES_EN`. Prefer `provideFormBuilderMessages()` over providing this directly. |
|
|
185
|
+
| `provideFormBuilderMessages(overrides)` | `(overrides: Partial<FormBuilderMessages>) => Provider` | Registers a message override, merged on top of the English defaults. |
|
|
186
|
+
| `FORM_BUILDER_MESSAGES_EN` | `FormBuilderMessages` | English preset (the default). |
|
|
187
|
+
| `FORM_BUILDER_MESSAGES_ES` | `FormBuilderMessages` | Spanish preset. |
|
|
66
188
|
|
|
67
189
|
### Theming
|
|
68
190
|
|
|
69
191
|
`FormBuilder` sizes and colors itself via CSS custom properties, part of
|
|
70
192
|
the shared `--zhunam-*` namespace used across every `@zhunam/*` library.
|
|
71
|
-
|
|
72
|
-
|
|
193
|
+
None of them are declared on the component's own `:host`, so you can set
|
|
194
|
+
any of them from `:root`, from a wrapping element, or scoped directly to
|
|
195
|
+
`lib-form-builder`, whichever is more convenient for your app; the
|
|
196
|
+
closest ancestor that sets a given property wins, same as any other
|
|
197
|
+
inherited CSS custom property.
|
|
73
198
|
|
|
74
199
|
| Custom property | Default | Description |
|
|
75
200
|
| ------------------------------- | -------- | -------------------------------------------------- |
|
|
76
|
-
| `--zhunam-primary` | `#3b82f6` | Submit button background, radio/checkbox accent color. |
|
|
201
|
+
| `--zhunam-primary` | `#3b82f6` | Submit button background, radio/checkbox accent color fallback. |
|
|
202
|
+
| `--zhunam-accent` | `var(--zhunam-primary)` | Radio/checkbox accent color specifically, independent from `--zhunam-primary`. Falls back to it when unset. |
|
|
203
|
+
| `--zhunam-submit-width` | `auto` | Submit button width. |
|
|
77
204
|
| `--zhunam-primary-content` | `#fff` | Submit button text/icon color, always painted on top of `--zhunam-primary`. |
|
|
78
205
|
| `--zhunam-focus` | `var(--zhunam-primary)` | Focus outline and border on controls, independent from `--zhunam-primary` so a high-contrast focus ring doesn't require changing your brand color. |
|
|
79
206
|
| `--zhunam-text` | `#1f2937` | Base text color, radio/checkbox option labels. |
|
|
80
207
|
| `--zhunam-text-secondary` | `#374151` | Field label text. |
|
|
81
208
|
| `--zhunam-border` | `#d1d5db` | Control borders. |
|
|
82
|
-
| `--zhunam-error` | `#dc2626` | Validation/server error message text. |
|
|
209
|
+
| `--zhunam-error` | `#dc2626` | Validation/server error message background/border, where used. See `--zhunam-error-text` for the message's own text color. |
|
|
210
|
+
| `--zhunam-error-text` | `var(--zhunam-error)` | Validation/server error message text color specifically. Falls back to `--zhunam-error` when unset, so most apps only ever need to set one of the two. |
|
|
83
211
|
| `--zhunam-radius` | `0.5rem` | Control and submit button corner radius. |
|
|
84
212
|
| `--zhunam-font-family` | `system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif` | Font for the whole component. |
|
|
85
213
|
| `--zhunam-font-size` | `0.9375rem` | Base font size. |
|
|
@@ -87,11 +215,48 @@ Set any of them from the consuming app's own stylesheet, scoped to
|
|
|
87
215
|
| `--zhunam-transition-duration` | `150ms` | Duration of border/opacity transitions. |
|
|
88
216
|
| `--zhunam-form-control-padding-x` | `0.75rem` | Horizontal padding inside inputs/selects/textareas. |
|
|
89
217
|
| `--zhunam-form-control-padding-y` | `0.5rem` | Vertical padding inside inputs/selects/textareas. |
|
|
218
|
+
| `--zhunam-segmented-bg` | `#fff` | Inactive pill background for a `'segmented'` radio group. The active pill reuses `--zhunam-primary`/`--zhunam-primary-content` instead, same as the submit button. |
|
|
90
219
|
|
|
91
220
|
`--fb-columns` is an internal implementation detail (it carries the
|
|
92
221
|
`columns` input's value into the field grid's CSS), not a themeable
|
|
93
222
|
custom property; setting it manually has no supported effect.
|
|
94
223
|
|
|
224
|
+
### Internationalization
|
|
225
|
+
|
|
226
|
+
Every message `FormBuilder` renders on its own (validation text, the
|
|
227
|
+
submit button label) comes from `FORM_BUILDER_MESSAGES`, an injectable
|
|
228
|
+
token that defaults to English. A per-field `FieldValidatorConfig.errorMessages`
|
|
229
|
+
override always takes precedence over this token for that specific field.
|
|
230
|
+
|
|
231
|
+
```typescript
|
|
232
|
+
import { provideFormBuilderMessages, FORM_BUILDER_MESSAGES_ES } from '@zhunam/form-builder';
|
|
233
|
+
|
|
234
|
+
// app.config.ts, or any component's own `providers`:
|
|
235
|
+
providers: [provideFormBuilderMessages(FORM_BUILDER_MESSAGES_ES)]
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
A partial override only replaces the messages you specify, the rest stay
|
|
239
|
+
in English:
|
|
240
|
+
|
|
241
|
+
```typescript
|
|
242
|
+
providers: [provideFormBuilderMessages({ submit: () => 'Send' })]
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
Every message is a function, called on every render rather than once at
|
|
246
|
+
startup, so one that reads a signal updates live:
|
|
247
|
+
|
|
248
|
+
```typescript
|
|
249
|
+
const language = signal<'en' | 'es'>('en');
|
|
250
|
+
|
|
251
|
+
providers: [
|
|
252
|
+
provideFormBuilderMessages({
|
|
253
|
+
required: () => (language() === 'en' ? 'This field is required.' : 'Este campo es obligatorio.'),
|
|
254
|
+
min: (min) =>
|
|
255
|
+
language() === 'en' ? `The value must be at least ${min}.` : `El valor debe ser como mínimo ${min}.`,
|
|
256
|
+
}),
|
|
257
|
+
]
|
|
258
|
+
```
|
|
259
|
+
|
|
95
260
|
## Compatibility
|
|
96
261
|
|
|
97
262
|
`@angular/core` and `@angular/forms` `^20.0.0 || ^21.0.0 || ^22.0.0`.
|