@modyra/angular 0.2.0 → 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/README.md CHANGED
@@ -9,33 +9,37 @@
9
9
  engine with native bindings for Angular, React, Vue and Lit — one shared
10
10
  core, one shared headless widget layer, one shared theme package.
11
11
 
12
- - No `FormControl`, `FormGroup` or RxJS — form state is signals and `computed`s
13
- - Compile-time checked field bindings: `[field]="form.f.email"`, typos don't compile
12
+ - No framework runtime, no RxJS — form state is plain signals and `computed`s
13
+ - Compile-time checked field bindings: `form.f.email` typos don't compile
14
14
  - Sync, async (debounced, cancellable, cross-field) and form-level validation
15
15
  - Typed field arrays (`array()`) for repeatable rows — push/insert/remove/move
16
16
  - Drafts (autosave/restore), undo/redo, minimal-patch change tracking, devtools
17
17
  - Headless core or accessible ready-made controls — your design system or ours
18
- - Incremental Angular adoption through Reactive Forms interop (`mdyCva`)
18
+ - Incremental adoption paths — e.g. Reactive Forms interop (`mdyCva`) on Angular
19
19
 
20
20
  [![CI](https://github.com/modyra/modyra/actions/workflows/ci.yml/badge.svg)](https://github.com/modyra/modyra/actions/workflows/ci.yml)
21
- [![npm](https://img.shields.io/npm/v/@modyra/angular)](https://www.npmjs.com/package/@modyra/angular)
22
- [![Angular](https://img.shields.io/badge/Angular-21%2B-red)](https://angular.dev)
21
+ [![Release](https://github.com/modyra/modyra/actions/workflows/release.yml/badge.svg?branch=main)](https://github.com/modyra/modyra/actions/workflows/release.yml)
22
+ [![npm](https://img.shields.io/npm/v/@modyra/core)](https://www.npmjs.com/package/@modyra/core)
23
23
  [![TypeScript](https://img.shields.io/badge/TypeScript-strict-blue)](https://www.typescriptlang.org)
24
24
  [![License](https://img.shields.io/badge/license-MIT-green)](LICENSE)
25
25
  [![GitHub Sponsors](https://img.shields.io/badge/Sponsor-GitHub-ea4aaa?style=flat&logo=github-sponsors)](https://github.com/sponsors/lorenzomusche)
26
26
 
27
27
  ## Packages
28
28
 
29
- | Package | What it is | Peer deps |
30
- | :------------------------------------ | :------------------------------------------------------------------------------------------------------- | :--------------------- |
31
- | [`@modyra/core`](packages/core) | Framework-agnostic form engine: typed field trees, arrays, validation, drafts, undo/redo, i18n utilities | — |
32
- | [`@modyra/widgets`](packages/widgets) | Headless widget controllers + universal interaction/accessibility contract | — |
33
- | [`@modyra/angular`](packages/angular) | Angular binding on native signals the most complete adapter (UI catalog, devtools, wizard, interop) | `@angular/*` ≥21 |
34
- | [`@modyra/react`](packages/react) | React binding via `useSyncExternalStore` | `react` ≥18 |
35
- | [`@modyra/vue`](packages/vue) | Vue binding on `@vue/reactivity` | `@vue/reactivity` ≥3.4 |
36
- | [`@modyra/lit`](packages/lit) | Lit binding — ReactiveController + themable form elements | `lit` ≥3 |
37
- | [`@modyra/zod`](packages/zod) | Framework-agnostic Zod adapter — schema-first typed forms | `zod` ≥3.25 |
38
- | [`@modyra/styles`](packages/styles) | CSS themes (`default`, `material`, `ios`, `ionic`, `base`) for every adapter | — |
29
+ | Package | What it is | UI layer | Peer deps |
30
+ | :---------------------------------------------------- | :------------------------------------------------------------------------------------------------------- | :------------------------------------------------------- | :--------------------- |
31
+ | [`@modyra/core`](packages/core) | Framework-agnostic form engine: typed field trees, arrays, validation, drafts, undo/redo, i18n utilities | headless | — |
32
+ | [`@modyra/widgets`](packages/widgets) | Headless widget controllers + universal interaction/accessibility contract | headless | — |
33
+ | [`@modyra/angular`](packages/angular) | Angular binding on native signals | full renderer catalog, themes, devtools, wizard, interop | `@angular/*` ≥21 |
34
+ | [`@modyra/react`](packages/react) | React binding via `useSyncExternalStore` | headless — bring your own UI | `react` ≥18 |
35
+ | [`@modyra/vue`](packages/vue) | Vue binding on `@vue/reactivity` | headless — bring your own UI | `@vue/reactivity` ≥3.4 |
36
+ | [`@modyra/lit`](packages/lit) | Lit binding — ReactiveController | themable form elements | `lit` ≥3 |
37
+ | [`@modyra/zod`](packages/zod) | Framework-agnostic Zod adapter — schema-first typed forms | — | `zod` ≥3.25 |
38
+ | [`@modyra/standard-schema`](packages/standard-schema) | Standard Schema adapter one adapter for Zod, Valibot, ArkType and every v1 vendor | — | — |
39
+ | [`@modyra/styles`](packages/styles) | CSS themes (`default`, `material`, `ios`, `ionic`, `base`) for every adapter | themes | — |
40
+
41
+ Every binding is a first-class citizen over the same engine: pick the one
42
+ for your framework, keep everything else identical.
39
43
 
40
44
  ## The engine in 60 seconds (framework-agnostic)
41
45
 
@@ -57,7 +61,7 @@ form.getValue().address.city; // "Rome" — fully typed, typos don't compile
57
61
  ```
58
62
 
59
63
  > **Validators are factories, not values:** write `required()`, not
60
- > `required` (Angular Reactive Forms muscle memory trips here — the
64
+ > `required` (value-style-validator muscle memory trips here — the
61
65
  > resulting TS error is easy to misread). Validation errors come back as
62
66
  > **arrays of message strings** (`["Name taken"]`), not `{ required: true }`
63
67
  > keyed objects. To stop at the first failing validator instead of
@@ -247,20 +251,15 @@ implemented end-to-end, side by side, in:
247
251
  Adapter recipes, the four-primitive reactive contract and the Astro note:
248
252
  [Multi-framework architecture](docs/guides/multi-framework.md).
249
253
 
250
- ## Why not Reactive Forms?
254
+ ## Coming from Angular Reactive Forms?
251
255
 
252
- Reactive Forms is official, mature and battle-tested if that is what your
253
- team needs, keep it. This library trades ecosystem maturity for:
254
- compile-checked field paths, signal-based state (zoneless-friendly, no RxJS)
255
- and built-in async/cross-field validation, typed arrays, drafts, undo/redo
256
- and devtools.
257
-
258
- "No RxJS / no `@angular/forms`" means precisely: no runtime dependency, no
259
- Observables in the public API, none used internally. The optional `/interop`
260
- entry point is the single exception — it declares `@angular/forms` as an
261
- _optional_ peer for CVA-based migration.
262
-
263
- Full, honest comparison: [Compared with Reactive Forms](docs/guides/comparison-reactive-forms.md).
256
+ This library trades ecosystem maturity for compile-checked field paths,
257
+ signal-based state (zoneless-friendly, no RxJS no runtime dependency, no
258
+ Observables in the public API, none used internally) and built-in
259
+ async/cross-field validation, typed arrays, drafts, undo/redo and devtools.
260
+ The `/interop` entry point (`mdyCva`, optional `@angular/forms` peer) covers
261
+ incremental adoption. Full, honest comparison:
262
+ [Compared with Reactive Forms](docs/guides/comparison-reactive-forms.md).
264
263
 
265
264
  ## Layers
266
265
 
@@ -300,8 +299,10 @@ The full index lives in [docs/README.md](docs/README.md). The shortlist:
300
299
 
301
300
  - [Mental model](docs/guides/mental-model.md) — the state graph, field lifecycle, operation semantics
302
301
  - [Typed forms](docs/guides/typed-forms.md) — schema, handles, `patch`/`getChanges`, async validation, field arrays, undo/redo, **drafts (read the security note)**, wizard, Zod
302
+ - [Schema adapters](docs/guides/schemas.md) — Zod vs Standard Schema (Valibot, ArkType, …): which model, which trade-offs
303
303
  - [Usage modes](docs/guides/usage-modes.md) — declarative, explicit adapter, headless, validation semantics
304
304
  - [UI toolkit](docs/guides/ui-toolkit.md) — renderer catalog, enterprise select, dynamic forms, CSS tokens
305
+ - [AI-generated forms](docs/guides/ai-generated-forms.md) — LLM output → `parseDynamicFields()` → render: the safe pipeline + system prompt template
305
306
  - [DevTools](docs/guides/devtools.md) — hotkey overlay, masking, production notes
306
307
  - [I18n](docs/guides/i18n.md) — UI strings (en/it/de/fr/es), date/time value models, localized parsing
307
308
  - [Reactive Forms interop](docs/guides/interop.md) · [Comparison](docs/guides/comparison-reactive-forms.md) · [Troubleshooting](docs/guides/troubleshooting.md)
@@ -317,15 +318,28 @@ Project policies: [security](SECURITY.md) · [contributing](CONTRIBUTING.md) ·
317
318
  own adapter package.
318
319
  - TypeScript strict mode; the library compiles with `strict` and
319
320
  `strictTemplates`.
320
- - Status: young library, actively developed, single maintainer. Unit and
321
- type tests cover the core engine, every adapter and the widget layer
322
- (`npm test`, `npm run test:core`, `npm run test:adapters`,
323
- `npm run test:widgets`), plus a tree-shaking bundle check
324
- (`npm run test:bundle`); browser, axe and visual tests are planned.
325
- Pin your version and read release notes.
321
+ - Status: young library, actively developed, single maintainer. `npm test`
322
+ runs the whole matrix core engine, every adapter, the widget layer and
323
+ the Angular package (`test:core`, `test:adapters`, `test:widgets`,
324
+ `test:angular` individually) plus a tree-shaking bundle check
325
+ (`npm run test:bundle`), axe-core accessibility tests over the main
326
+ Angular renderers (jest + jsdom, inside `test:angular`) and a Playwright
327
+ browser smoke test over the packaged Angular demo (`npm run test:e2e`,
328
+ currently non-blocking in CI while it stabilizes); visual regression
329
+ tests are still planned. Pin your version and read release notes.
326
330
 
327
331
  ## Examples
328
332
 
333
+ [![Open in StackBlitz](https://developer.stackblitz.com/img/open_in_stackblitz.svg)](https://stackblitz.com/github/modyra/modyra/tree/main/examples/stackblitz)
334
+
335
+ `examples/stackblitz` is a minimal Angular signup form that runs against the
336
+ **published** `@modyra/angular` package — the fastest way to try the library
337
+ without cloning anything.
338
+
339
+ `examples/angular` is the full Angular demo app (typed, declarative, dynamic
340
+ and Zod sections over the whole renderer catalog) — one demo app per
341
+ framework, same engine.
342
+
329
343
  `examples/{react,vue,lit}` implement the **same signup form** (name +
330
344
  email, shared validators, agnostic devtools panel) so the adapters can be
331
345
  compared side by side, with a runtime switcher across the shipped themes
@@ -344,11 +358,10 @@ npm run demo:lit # http://localhost:4303
344
358
 
345
359
  ```bash
346
360
  pnpm install # workspace deps use the workspace: protocol — use pnpm
347
- npm run build:lib # core + widgets + zod + Angular library build
348
- npm run build:packages # core + widgets + zod/vue/react/lit + styles
349
- npm start # Angular demo app
350
- npm test # Angular unit + type tests
351
- npm run test:adapters # zod/vue/react/lit node tests
361
+ npm run build:packages # core + widgets + schema adapters + react/vue/lit + styles
362
+ npm run build:angular # the Angular package (kept as build:lib alias)
363
+ npm test # the whole matrix: core, adapters, widgets, Angular
364
+ npm run demo:angular # one demo per framework: demo:react / demo:vue / demo:lit
352
365
  ```
353
366
 
354
367
  ## Brand
@@ -1,8 +1,10 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { InjectionToken, untracked, effect, computed, signal, inject, Injector, DestroyRef, input, output, forwardRef, ChangeDetectionStrategy, Component, linkedSignal, contentChildren, booleanAttribute, ApplicationRef, EnvironmentInjector, createComponent, Injectable, ElementRef, Directive, TemplateRef, contentChild, viewChild, viewChildren, afterNextRender, Renderer2, numberAttribute, HostAttributeToken } from '@angular/core';
3
- import { MDY_I18N_MESSAGES_DEFAULT, buildDateLocale, MDY_I18N_PRESETS, MDY_I18N_DEFAULT_TAGS, MdyFormEngine, field as field$1, group as group$1, array as array$1, MdyTypedFormBase, email, maxLength, max, minLength, min, pattern, required } from '@modyra/core';
3
+ import { MDY_I18N_MESSAGES_DEFAULT, MDY_I18N_PRESETS, MDY_I18N_DEFAULT_TAGS } from '@modyra/core/localization';
4
+ import { buildDateLocale } from '@modyra/core/datetime';
5
+ import { MdyFormEngine, field as field$1, group as group$1, array as array$1, MdyTypedFormBase, email, maxLength, max, minLength, min, pattern, required } from '@modyra/core';
4
6
  import { mdyFormSerialize } from '@modyra/core/serialize';
5
- import { buildDynamicValidators } from '@modyra/core/dynamic-config';
7
+ import { buildDynamicFieldValidators } from '@modyra/core/dynamic-config';
6
8
  import { NgTemplateOutlet, NgClass, NgStyle } from '@angular/common';
7
9
  import { today, buildMonthGrid, isSameDay, isDateInRange, daysInMonth, addMonths, parseIsoDate, formatIsoDate, parseLocalizedDate, orderDates, isDateBetween, compareDates } from '@modyra/core/date-utils';
8
10
  import { DomSanitizer } from '@angular/platform-browser';
@@ -148,9 +150,12 @@ function angularReactivity(injector) {
148
150
  class MdyDeclarativeAdapter extends MdyFormEngine {
149
151
  constructor(formValue, submitMode = signal("valid-only"),
150
152
  /** Needed to run async validators, drafts and history. */
151
- injector) {
153
+ injector,
154
+ /** Injection-prevention policy for field values (see `@modyra/core` security). */
155
+ security) {
152
156
  super(angularReactivity(injector), formValue, submitMode, {
153
157
  devWarnings: typeof ngDevMode !== "undefined" && !!ngDevMode,
158
+ ...(security !== undefined && { security }),
154
159
  });
155
160
  }
156
161
  getField(name) {
@@ -209,6 +214,7 @@ function mdyForm(schema, options) {
209
214
  ...(options?.validators !== undefined && { validators: options.validators }),
210
215
  ...(options?.history !== undefined && { history: options.history }),
211
216
  ...(options?.draft !== undefined && { draft: options.draft }),
217
+ ...(options?.security !== undefined && { security: options.security }),
212
218
  ...(injector !== undefined && { injector }),
213
219
  });
214
220
  // Tie the form's resources (async runners, draft/history timers) to the
@@ -226,7 +232,7 @@ function mdyForm(schema, options) {
226
232
  */
227
233
  class MdyTypedForm extends MdyTypedFormBase {
228
234
  constructor(schema, options) {
229
- const adapter = new MdyDeclarativeAdapter(signal(undefined), signal(options?.submitMode ?? "valid-only"), options?.injector);
235
+ const adapter = new MdyDeclarativeAdapter(signal(undefined), signal(options?.submitMode ?? "valid-only"), options?.injector, options?.security);
230
236
  super(schema, adapter, options);
231
237
  this.state = adapter.state;
232
238
  this.value = computed(() => this._flatToValue(this._adapter.value()));
@@ -445,6 +451,9 @@ class MdyFormComponent {
445
451
  setInitialValue(name, value) {
446
452
  this._registry.setInitialValue(name, value);
447
453
  }
454
+ setSanitizer(name, sanitizer) {
455
+ this._registry.setSanitizer(name, sanitizer);
456
+ }
448
457
  setDisabled(name, disabled) {
449
458
  this._registry.setDisabled(name, disabled);
450
459
  }
@@ -3193,7 +3202,6 @@ class MdyDatePickerComponent extends MdyOverlayControl {
3193
3202
  [disabled]="isDisabled()"
3194
3203
  (change)="onInputChange($event)"
3195
3204
  (blur)="onInputBlur($event)"
3196
- [attr.aria-expanded]="open()"
3197
3205
  [attr.aria-haspopup]="'dialog'"
3198
3206
  [attr.aria-invalid]="hasErrors()"
3199
3207
  [attr.aria-describedby]="hasErrors() ? fieldId + '-errors' : null"
@@ -3211,6 +3219,8 @@ class MdyDatePickerComponent extends MdyOverlayControl {
3211
3219
  [disabled]="isDisabled()"
3212
3220
  (click)="toggleOverlay($event)"
3213
3221
  [attr.aria-label]="i18n.datepickerToggleLabel"
3222
+ [attr.aria-expanded]="open()"
3223
+ [attr.aria-haspopup]="'dialog'"
3214
3224
  >
3215
3225
  <mdy-icon name="CALENDAR" class="mdy-datepicker__icon" />
3216
3226
  </button>
@@ -3311,7 +3321,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImpo
3311
3321
  [disabled]="isDisabled()"
3312
3322
  (change)="onInputChange($event)"
3313
3323
  (blur)="onInputBlur($event)"
3314
- [attr.aria-expanded]="open()"
3315
3324
  [attr.aria-haspopup]="'dialog'"
3316
3325
  [attr.aria-invalid]="hasErrors()"
3317
3326
  [attr.aria-describedby]="hasErrors() ? fieldId + '-errors' : null"
@@ -3329,6 +3338,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImpo
3329
3338
  [disabled]="isDisabled()"
3330
3339
  (click)="toggleOverlay($event)"
3331
3340
  [attr.aria-label]="i18n.datepickerToggleLabel"
3341
+ [attr.aria-expanded]="open()"
3342
+ [attr.aria-haspopup]="'dialog'"
3332
3343
  >
3333
3344
  <mdy-icon name="CALENDAR" class="mdy-datepicker__icon" />
3334
3345
  </button>
@@ -6493,7 +6504,6 @@ class MdyTimepickerComponent extends MdyOverlayControl {
6493
6504
  [attr.aria-required]="ariaRequired() || isRequired()"
6494
6505
  [attr.aria-disabled]="effectiveAriaDisabled()"
6495
6506
  [attr.aria-label]="label() || null"
6496
- [attr.aria-expanded]="open()"
6497
6507
  [attr.aria-haspopup]="'dialog'"
6498
6508
  autocomplete="off"
6499
6509
  />
@@ -6506,6 +6516,8 @@ class MdyTimepickerComponent extends MdyOverlayControl {
6506
6516
  class="mdy-timepicker__toggle"
6507
6517
  [disabled]="isDisabled()"
6508
6518
  [attr.aria-label]="i18n.timepickerOpenLabel"
6519
+ [attr.aria-expanded]="open()"
6520
+ [attr.aria-haspopup]="'dialog'"
6509
6521
  tabindex="-1"
6510
6522
  (click)="toggleOverlay($event)"
6511
6523
  >
@@ -6595,7 +6607,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImpo
6595
6607
  [attr.aria-required]="ariaRequired() || isRequired()"
6596
6608
  [attr.aria-disabled]="effectiveAriaDisabled()"
6597
6609
  [attr.aria-label]="label() || null"
6598
- [attr.aria-expanded]="open()"
6599
6610
  [attr.aria-haspopup]="'dialog'"
6600
6611
  autocomplete="off"
6601
6612
  />
@@ -6608,6 +6619,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImpo
6608
6619
  class="mdy-timepicker__toggle"
6609
6620
  [disabled]="isDisabled()"
6610
6621
  [attr.aria-label]="i18n.timepickerOpenLabel"
6622
+ [attr.aria-expanded]="open()"
6623
+ [attr.aria-haspopup]="'dialog'"
6611
6624
  tabindex="-1"
6612
6625
  (click)="toggleOverlay($event)"
6613
6626
  >
@@ -6791,7 +6804,9 @@ class MdyDynamicFormComponent {
6791
6804
  const form = this.form();
6792
6805
  untracked(() => {
6793
6806
  for (const f of fields) {
6794
- const { validators, marksRequired } = buildDynamicValidators(f.validators ?? {});
6807
+ // buildDynamicFieldValidators includes the automatic option
6808
+ // whitelist (anti-tampering) for select/radio/segmented/multiselect.
6809
+ const { validators, marksRequired } = buildDynamicFieldValidators(f);
6795
6810
  form.upsertValidators(f.name, "mdy-dynamic", validators, marksRequired);
6796
6811
  }
6797
6812
  });
@@ -8718,7 +8733,6 @@ class MdyDateRangePickerComponent extends MdyOverlayControl {
8718
8733
  [attr.aria-required]="ariaRequired() || isRequired()"
8719
8734
  [attr.aria-disabled]="effectiveAriaDisabled()"
8720
8735
  [attr.aria-label]="(label() ? label() + ' — ' : '') + i18n.daterangeStartLabel"
8721
- [attr.aria-expanded]="open()"
8722
8736
  [attr.aria-haspopup]="'dialog'"
8723
8737
  autocomplete="off"
8724
8738
  />
@@ -8742,7 +8756,6 @@ class MdyDateRangePickerComponent extends MdyOverlayControl {
8742
8756
  [attr.aria-required]="ariaRequired() || isRequired()"
8743
8757
  [attr.aria-disabled]="effectiveAriaDisabled()"
8744
8758
  [attr.aria-label]="(label() ? label() + ' — ' : '') + i18n.daterangeEndLabel"
8745
- [attr.aria-expanded]="open()"
8746
8759
  [attr.aria-haspopup]="'dialog'"
8747
8760
  autocomplete="off"
8748
8761
  />
@@ -8753,6 +8766,8 @@ class MdyDateRangePickerComponent extends MdyOverlayControl {
8753
8766
  class="mdy-datepicker__toggle"
8754
8767
  [disabled]="isDisabled()"
8755
8768
  [attr.aria-label]="i18n.datepickerToggleLabel"
8769
+ [attr.aria-expanded]="open()"
8770
+ [attr.aria-haspopup]="'dialog'"
8756
8771
  tabindex="-1"
8757
8772
  (click)="toggleOverlay($event)"
8758
8773
  >
@@ -8862,7 +8877,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImpo
8862
8877
  [attr.aria-required]="ariaRequired() || isRequired()"
8863
8878
  [attr.aria-disabled]="effectiveAriaDisabled()"
8864
8879
  [attr.aria-label]="(label() ? label() + ' — ' : '') + i18n.daterangeStartLabel"
8865
- [attr.aria-expanded]="open()"
8866
8880
  [attr.aria-haspopup]="'dialog'"
8867
8881
  autocomplete="off"
8868
8882
  />
@@ -8886,7 +8900,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImpo
8886
8900
  [attr.aria-required]="ariaRequired() || isRequired()"
8887
8901
  [attr.aria-disabled]="effectiveAriaDisabled()"
8888
8902
  [attr.aria-label]="(label() ? label() + ' — ' : '') + i18n.daterangeEndLabel"
8889
- [attr.aria-expanded]="open()"
8890
8903
  [attr.aria-haspopup]="'dialog'"
8891
8904
  autocomplete="off"
8892
8905
  />
@@ -8897,6 +8910,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImpo
8897
8910
  class="mdy-datepicker__toggle"
8898
8911
  [disabled]="isDisabled()"
8899
8912
  [attr.aria-label]="i18n.datepickerToggleLabel"
8913
+ [attr.aria-expanded]="open()"
8914
+ [attr.aria-haspopup]="'dialog'"
8900
8915
  tabindex="-1"
8901
8916
  (click)="toggleOverlay($event)"
8902
8917
  >
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@modyra/angular",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Type-safe Angular forms built entirely on native Signals.",
5
5
  "funding": {
6
6
  "type": "github",
@@ -50,8 +50,8 @@
50
50
  ],
51
51
  "dependencies": {
52
52
  "tslib": ">=2.3.0",
53
- "@modyra/core": "^0.2.0",
54
- "@modyra/widgets": "^0.2.0"
53
+ "@modyra/core": "^0.3.0",
54
+ "@modyra/widgets": "^0.3.0"
55
55
  },
56
56
  "exports": {
57
57
  ".": {
@@ -1,11 +1,13 @@
1
1
  import * as _angular_core from '@angular/core';
2
2
  import { Signal, WritableSignal, Injector, InjectionToken, Provider, TemplateRef, OnInit, ElementRef, InputSignal, OnDestroy, InputSignalWithTransform } from '@angular/core';
3
3
  import * as _modyra_core from '@modyra/core';
4
- import { MdyFormError, MdyFieldError, MdyFormSubmitEvent, MdyControlOption, ValidatorFn, MdySelectOption, MdyFormRegistry, MdyFormEngine, MdySubmitMode, MdyI18nMessages, MdyBuiltInLocale, MdyDateLocale, MdyAnyArrayDescriptor as MdyAnyArrayDescriptor$1, MdyAnyFieldDescriptor as MdyAnyFieldDescriptor$1, MdyAnyGroupDescriptor as MdyAnyGroupDescriptor$1, MdyArrayDescriptor as MdyArrayDescriptor$1, MdyArrayItemValue as MdyArrayItemValue$1, MdyFieldDescriptor as MdyFieldDescriptor$1, MdyFormSchema as MdyFormSchema$1, MdyGroupDescriptor as MdyGroupDescriptor$1, MdyFieldOptions as MdyFieldOptions$1, MdyTypedFormBaseOptions, MdyFormPatch as MdyFormPatch$1, MdyFormValue as MdyFormValue$1, MdyTypedFormBase, MdyWiden as MdyWiden$1, MdyFormValidatorFn, MdyAsyncValidatorFn, MdyAsyncValidatorOptions, MdyDateRange, MdyReactivity } from '@modyra/core';
5
- export { MdyAsyncValidatorFn, MdyAsyncValidatorOptions, MdyControlOption, MdyDateRange, MdyDraftOptions, MdyDraftStorage, MdyFieldError, MdyFormError, MdyFormSubmitEvent, MdyFormValidatorFn, MdySelectOption, MdySubmitMode, ValidatorFn } from '@modyra/core';
4
+ import { MdyFormError, MdyFieldError, MdyFormSubmitEvent, MdyControlOption, ValidatorFn, MdySelectOption, MdyFormRegistry, MdyFormEngine, MdySubmitMode, MdySecurityPolicy, MdyAnyArrayDescriptor as MdyAnyArrayDescriptor$1, MdyAnyFieldDescriptor as MdyAnyFieldDescriptor$1, MdyAnyGroupDescriptor as MdyAnyGroupDescriptor$1, MdyArrayDescriptor as MdyArrayDescriptor$1, MdyArrayItemValue as MdyArrayItemValue$1, MdyFieldDescriptor as MdyFieldDescriptor$1, MdyFormSchema as MdyFormSchema$1, MdyGroupDescriptor as MdyGroupDescriptor$1, MdyFieldOptions as MdyFieldOptions$1, MdyTypedFormBaseOptions, MdyFormPatch as MdyFormPatch$1, MdyFormValue as MdyFormValue$1, MdyTypedFormBase, MdyWiden as MdyWiden$1, MdyFormValidatorFn, MdyAsyncValidatorFn, MdyAsyncValidatorOptions, MdySanitizer, MdyDateRange, MdyReactivity } from '@modyra/core';
5
+ export { MdyAsyncValidatorFn, MdyAsyncValidatorOptions, MdyControlOption, MdyDateRange, MdyDraftOptions, MdyDraftStorage, MdyFieldError, MdyFormError, MdyFormSubmitEvent, MdyFormValidatorFn, MdySanitizeProfile, MdySanitizer, MdySecurityPolicy, MdySecurityViolation, MdySecurityViolationKind, MdySelectOption, MdySubmitMode, ValidatorFn } from '@modyra/core';
6
6
  import * as _modyra_angular from '@modyra/angular';
7
+ import { MdyI18nMessages, MdyBuiltInLocale } from '@modyra/core/localization';
8
+ import { MdyDateLocale } from '@modyra/core/datetime';
7
9
  import { MdyDynamicField } from '@modyra/core/dynamic-config';
8
- import * as packages_core_dist from 'packages/core/dist';
10
+ import * as packages_core_dist_i18n from 'packages/core/dist/i18n';
9
11
  import { OverlayPosition, OverlayAlignment, OverlayAnchor } from '@modyra/core/overlay-position';
10
12
  import { CalendarCell, CalendarDate } from '@modyra/core/date-utils';
11
13
  import * as _modyra_widgets from '@modyra/widgets';
@@ -122,7 +124,9 @@ type MdyDeclarativeRegistry = MdyFormRegistry<Signal<boolean>>;
122
124
  declare class MdyDeclarativeAdapter extends MdyFormEngine implements MdyFormAdapter<Record<string, unknown>>, MdyDeclarativeRegistry {
123
125
  constructor(formValue: Signal<Record<string, unknown> | undefined>, submitMode?: Signal<MdySubmitMode>,
124
126
  /** Needed to run async validators, drafts and history. */
125
- injector?: Injector);
127
+ injector?: Injector,
128
+ /** Injection-prevention policy for field values (see `@modyra/core` security). */
129
+ security?: MdySecurityPolicy);
126
130
  readonly state: MdyFormState;
127
131
  readonly value: Signal<Record<string, unknown>>;
128
132
  readonly fieldNames: Signal<readonly string[]>;
@@ -467,6 +471,7 @@ declare class MdyFormComponent<T extends Record<string, unknown>> implements Mdy
467
471
  removeValidators(name: string, key: string): void;
468
472
  upsertAsyncValidators<V>(name: string, key: string, validators: ReadonlyArray<MdyAsyncValidatorFn<V>>, options?: MdyAsyncValidatorOptions): void;
469
473
  setInitialValue(name: string, value: unknown): void;
474
+ setSanitizer(name: string, sanitizer: MdySanitizer): void;
470
475
  setDisabled(name: string, disabled: Signal<boolean>): void;
471
476
  setReadonly(name: string, readonly: Signal<boolean>): void;
472
477
  get state(): MdyFormState;
@@ -723,7 +728,7 @@ declare class MdyWizardStepComponent {
723
728
  * `form.submit(...)` or submit the surrounding form there.
724
729
  */
725
730
  declare class MdyFormWizardComponent {
726
- protected readonly i18n: packages_core_dist.MdyI18nMessages;
731
+ protected readonly i18n: packages_core_dist_i18n.MdyI18nMessages;
727
732
  private readonly adapter;
728
733
  protected readonly steps: Signal<readonly MdyWizardStepComponent[]>;
729
734
  private readonly _activeIndex;
@@ -1080,7 +1085,7 @@ declare class MdyGlassDirective {
1080
1085
  /** Optional override for the blur amount (e.g., '10px' or 'blur(10px)') */
1081
1086
  readonly blur: _angular_core.InputSignal<string | undefined>;
1082
1087
  /** Intensity of the glass effect. Themes can use this to adjust blur/opacity. */
1083
- readonly intensity: _angular_core.InputSignal<"low" | "medium" | "high">;
1088
+ readonly intensity: _angular_core.InputSignal<"high" | "low" | "medium">;
1084
1089
  /** Optional background color override to tint the glass. */
1085
1090
  readonly glassColor: _angular_core.InputSignal<string | undefined>;
1086
1091
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<MdyGlassDirective, never>;
@@ -1341,7 +1346,7 @@ declare class MdyCheckboxComponent extends MdyBaseControl<boolean> {
1341
1346
  * ```
1342
1347
  */
1343
1348
  declare class MdyColorsComponent extends MdyOverlayControl<string> {
1344
- protected readonly i18n: packages_core_dist.MdyI18nMessages;
1349
+ protected readonly i18n: packages_core_dist_i18n.MdyI18nMessages;
1345
1350
  readonly placeholder: _angular_core.InputSignal<string>;
1346
1351
  readonly presets: _angular_core.InputSignal<readonly string[]>;
1347
1352
  protected readonly fieldId: string;
@@ -1463,7 +1468,7 @@ declare class MdyCalendarHeaderComponent {
1463
1468
  readonly nextMonth: _angular_core.OutputEmitterRef<void>;
1464
1469
  readonly toggleView: _angular_core.OutputEmitterRef<void>;
1465
1470
  private readonly locale;
1466
- protected readonly i18n: packages_core_dist.MdyI18nMessages;
1471
+ protected readonly i18n: packages_core_dist_i18n.MdyI18nMessages;
1467
1472
  /** Localized month name (e.g. "March", "Marzo"). */
1468
1473
  protected readonly monthLabel: _angular_core.Signal<string>;
1469
1474
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<MdyCalendarHeaderComponent, never>;
@@ -1547,7 +1552,7 @@ declare class MdyDatePickerComponent extends MdyOverlayControl<string | null> {
1547
1552
  readonly displayFormat: _angular_core.InputSignal<"iso" | "localized">;
1548
1553
  protected readonly minSpace = 450;
1549
1554
  protected readonly fieldId: string;
1550
- protected readonly i18n: packages_core_dist.MdyI18nMessages;
1555
+ protected readonly i18n: packages_core_dist_i18n.MdyI18nMessages;
1551
1556
  private readonly calendarRef;
1552
1557
  private readonly locale;
1553
1558
  private readonly injector;
@@ -1586,7 +1591,7 @@ declare class MdyDateRangePickerComponent extends MdyOverlayControl<MdyDateRange
1586
1591
  private readonly calendarRef;
1587
1592
  private readonly locale;
1588
1593
  private readonly injector;
1589
- protected readonly i18n: packages_core_dist.MdyI18nMessages;
1594
+ protected readonly i18n: packages_core_dist_i18n.MdyI18nMessages;
1590
1595
  protected readonly tempStart: _angular_core.WritableSignal<CalendarDate | null>;
1591
1596
  protected readonly tempEnd: _angular_core.WritableSignal<CalendarDate | null>;
1592
1597
  protected readonly displayStart: _angular_core.Signal<string>;
@@ -1751,7 +1756,7 @@ declare class MdyFileComponent extends MdyBaseControl<File | File[] | null> {
1751
1756
  readonly fileSelected: _angular_core.OutputEmitterRef<File | File[] | null>;
1752
1757
  /** Emits the files rejected by accept/maxFileSize/maxFiles filtering. */
1753
1758
  readonly filesRejected: _angular_core.OutputEmitterRef<readonly File[]>;
1754
- protected readonly i18n: packages_core_dist.MdyI18nMessages;
1759
+ protected readonly i18n: packages_core_dist_i18n.MdyI18nMessages;
1755
1760
  protected readonly fieldId: string;
1756
1761
  protected readonly dragOver: _angular_core.WritableSignal<boolean>;
1757
1762
  private readonly fileInput;
@@ -1779,7 +1784,7 @@ declare class MdyFileComponent extends MdyBaseControl<File | File[] | null> {
1779
1784
  */
1780
1785
  declare abstract class MdyDropdownBase<TValue, TOptionValue = unknown> extends MdyOptionsOverlayControl<TValue, TOptionValue> implements MdyOptionsControl<TOptionValue> {
1781
1786
  /** Internationalized strings used by dropdown UI. */
1782
- protected readonly i18n: packages_core_dist.MdyI18nMessages;
1787
+ protected readonly i18n: packages_core_dist_i18n.MdyI18nMessages;
1783
1788
  /** Injector for Angular effects and `afterNextRender` calls. */
1784
1789
  protected readonly injector: Injector;
1785
1790
  /** Minimum viewport space required to anchor the dropdown. */
@@ -1803,7 +1808,7 @@ declare abstract class MdyDropdownBase<TValue, TOptionValue = unknown> extends M
1803
1808
  * Renders selectable filter chips and delivers ReadonlyArray<TValue>.
1804
1809
  */
1805
1810
  declare class MdyMultiselectComponent<TValue = string> extends MdyDropdownBase<ReadonlyArray<TValue>, TValue> implements MdyOptionsControl<TValue> {
1806
- readonly mode: _angular_core.InputSignal<"single" | "multi">;
1811
+ readonly mode: _angular_core.InputSignal<"multi" | "single">;
1807
1812
  /**
1808
1813
  * Optional filter predicate applied to options before display.
1809
1814
  * Receives the option *value* and returns `true` to show the option.
@@ -2013,7 +2018,7 @@ declare class MdyToggleComponent extends MdyBaseControl<boolean> {
2013
2018
  * Timepicker renderer — M3-style input with clock overlay.
2014
2019
  */
2015
2020
  declare class MdyTimepickerComponent extends MdyOverlayControl<string | null> {
2016
- protected readonly i18n: packages_core_dist.MdyI18nMessages;
2021
+ protected readonly i18n: packages_core_dist_i18n.MdyI18nMessages;
2017
2022
  readonly placeholder: _angular_core.InputSignal<string>;
2018
2023
  /**
2019
2024
  * Value and display format: `"12h"` uses `"hh:mm AM/PM"` strings,