@modyra/angular 0.2.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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 lorenzo muscherà
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,364 @@
1
+ <picture>
2
+ <source media="(prefers-color-scheme: dark)" srcset="docs/assets/brand/readme-banner-dark.png">
3
+ <img src="docs/assets/brand/readme-banner-light.png" alt="Modyra — Model once. Render anywhere.">
4
+ </picture>
5
+
6
+ # Modyra
7
+
8
+ **Model once. Render anywhere.** A framework-agnostic, type-safe form
9
+ engine with native bindings for Angular, React, Vue and Lit — one shared
10
+ core, one shared headless widget layer, one shared theme package.
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
14
+ - Sync, async (debounced, cancellable, cross-field) and form-level validation
15
+ - Typed field arrays (`array()`) for repeatable rows — push/insert/remove/move
16
+ - Drafts (autosave/restore), undo/redo, minimal-patch change tracking, devtools
17
+ - Headless core or accessible ready-made controls — your design system or ours
18
+ - Incremental Angular adoption through Reactive Forms interop (`mdyCva`)
19
+
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)
23
+ [![TypeScript](https://img.shields.io/badge/TypeScript-strict-blue)](https://www.typescriptlang.org)
24
+ [![License](https://img.shields.io/badge/license-MIT-green)](LICENSE)
25
+ [![GitHub Sponsors](https://img.shields.io/badge/Sponsor-GitHub-ea4aaa?style=flat&logo=github-sponsors)](https://github.com/sponsors/lorenzomusche)
26
+
27
+ ## Packages
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 | — |
39
+
40
+ ## The engine in 60 seconds (framework-agnostic)
41
+
42
+ Everything below runs in plain TypeScript — Node, a CLI, a worker, a unit
43
+ test, or any of the four framework adapters. No framework in sight:
44
+
45
+ ```ts
46
+ import { createForm, field, group, required, email, min } from "@modyra/core";
47
+
48
+ const form = createForm({
49
+ email: field("", [required(), email()]),
50
+ age: field<number | null>(null, [min(18)]),
51
+ address: group({ city: field("Rome"), zip: field("") }),
52
+ });
53
+
54
+ form.f.email.set("not-an-email");
55
+ form.f.email.errors(); // ["Enter a valid email address"]
56
+ form.getValue().address.city; // "Rome" — fully typed, typos don't compile
57
+ ```
58
+
59
+ > **Validators are factories, not values:** write `required()`, not
60
+ > `required` (Angular Reactive Forms muscle memory trips here — the
61
+ > resulting TS error is easy to misread). Validation errors come back as
62
+ > **arrays of message strings** (`["Name taken"]`), not `{ required: true }`
63
+ > keyed objects. To stop at the first failing validator instead of
64
+ > collecting all of them, use `composeFirst()` in place of `compose()`.
65
+
66
+ ## Real-world scenarios, handled by the engine
67
+
68
+ These are the cases that make form code rot in production — nested data,
69
+ repeatable rows, server round-trips, refreshes, "apply my changes only".
70
+ Each one below is complete and runnable in plain TypeScript.
71
+
72
+ ### 1. Checkout: nested groups, repeatable line items, a coupon checked server-side
73
+
74
+ An order form with an address group, a **typed array** of line items, a
75
+ coupon validated against the server (re-checked automatically when the
76
+ country changes), and server errors routed back to fields on submit.
77
+
78
+ ```ts
79
+ import {
80
+ createForm,
81
+ field,
82
+ group,
83
+ array,
84
+ required,
85
+ min,
86
+ pattern,
87
+ crossField,
88
+ serverValidator,
89
+ } from "@modyra/core";
90
+
91
+ const form = createForm(
92
+ {
93
+ country: field("IT"),
94
+ shipping: group({
95
+ city: field("", [required()]),
96
+ zip: field("", [required(), pattern(/^\d{5}$/, "5 digits")]),
97
+ }),
98
+ // Typed repeatable rows: form.f.items.rows(), push/insert/remove/move
99
+ items: array(
100
+ group({
101
+ sku: field("", [required()]),
102
+ qty: field<number>(1, [min(1)]),
103
+ }),
104
+ { initial: [{ sku: "TSHIRT-BLK-M", qty: 2 }] },
105
+ ),
106
+ coupon: field(
107
+ "",
108
+ [],
109
+ serverValidator(
110
+ async (code, ctx) => {
111
+ if (!code) return null; // optional field — skip the call
112
+ // ctx.form reads the rest of the form; ctx.signal cancels stale calls
113
+ const res = await api.coupons.check(
114
+ code,
115
+ ctx.form.fieldValue("country"),
116
+ {
117
+ signal: ctx.signal,
118
+ },
119
+ );
120
+ return res.valid ? null : "Coupon not valid for your country";
121
+ },
122
+ {
123
+ dependsOn: ["country"], // country flips → coupon re-validates itself
124
+ debounceMs: 400,
125
+ timeoutMs: 5000, // never a "pending" that lasts forever
126
+ },
127
+ ),
128
+ ),
129
+ },
130
+ {
131
+ validators: [
132
+ // Form-level rule over the whole typed value
133
+ crossField(["items"], (v) =>
134
+ v.items.length === 0 ? "Add at least one item to the order" : null,
135
+ ),
136
+ ],
137
+ },
138
+ );
139
+
140
+ form.f.items.push({ sku: "MUG-WHT", qty: 1 });
141
+ form.f.items.rows()[1].sku.errors(); // per-row, per-field errors
142
+ form.f.items.remove(0);
143
+ form.f.items.length(); // 1
144
+ form.getValue().items[0].qty; // number — a typo here is a compile error
145
+
146
+ const result = await form.submit(async (order) => {
147
+ const res = await api.orders.create(order);
148
+ if (!res.ok) {
149
+ // Server errors land on the matching field (or the form, with path: null)
150
+ return res.errors.map((e) => ({
151
+ path: e.field,
152
+ kind: "server",
153
+ message: e.message,
154
+ }));
155
+ }
156
+ });
157
+ ```
158
+
159
+ What the engine did for you: `items.0.sku`-style paths stay compile-checked;
160
+ `state.valid()` includes per-row validators _and_ the form-level rule; the
161
+ coupon re-checks itself when `country` flips and aborts the previous HTTP
162
+ call when you keep typing; `state.pending()` covers every async run, so a
163
+ submit button bound to `state.canSubmit()` can't fire mid-validation.
164
+
165
+ ### 2. A long insurance claim: survive refresh, undo mistakes, patch only what changed
166
+
167
+ A 40-field claim form. The user refreshes mid-way (drafts), deletes the
168
+ wrong paragraph (undo/redo), and your backend only wants the diff
169
+ (`getChanges`).
170
+
171
+ ```ts
172
+ import { createForm, field, group, required, minLength } from "@modyra/core";
173
+
174
+ const form = createForm(
175
+ {
176
+ policyNumber: field("", [required()]),
177
+ incident: group({
178
+ date: field("", [required()]), // ISO yyyy-MM-dd
179
+ description: field("", [required(), minLength(30)]),
180
+ }),
181
+ iban: field("", [required()]),
182
+ },
183
+ {
184
+ // Autosave to localStorage every 500 ms of idle; restored on reload.
185
+ // Sensitive fields are masked in devtools and excluded from the draft.
186
+ draft: { key: "claim-form", exclude: ["iban"] },
187
+ history: true, // undo()/redo()
188
+ },
189
+ );
190
+
191
+ form.f.incident.description.set("The kitchen pipe burst and…");
192
+ form.undo(); // oops — bring the paragraph back
193
+ form.redo();
194
+
195
+ form.getChanges(); // { incident: { description: "The kitchen pipe…" } }
196
+ // → the minimal PATCH body, typed
197
+ ```
198
+
199
+ Drafts are versioned envelopes with a 7-day TTL: File/BigInt values are
200
+ refused, quota errors never crash the form, and `__proto__`-style paths in
201
+ a tampered `localStorage` entry are discarded at restore.
202
+
203
+ ### 3. Schema-first: the backend already speaks Zod
204
+
205
+ The validation contract lives in one Zod schema shared with your API — the
206
+ form derives fields, initial values, validators and cross-field rules from
207
+ it, arrays included:
208
+
209
+ ```ts
210
+ import { z } from "zod";
211
+ import { createZodForm } from "@modyra/zod";
212
+
213
+ const passengerSchema = z.object({
214
+ fullName: z.string().min(1, "Required"),
215
+ infant: z.boolean(),
216
+ });
217
+
218
+ const bookingSchema = z.object({
219
+ flight: z.string().min(1, "Pick a flight"),
220
+ passengers: z.array(passengerSchema).min(1, "At least one passenger"),
221
+ contact: z.object({
222
+ email: z.string().email(),
223
+ phone: z.string().optional(),
224
+ }),
225
+ });
226
+
227
+ const form = createZodForm(bookingSchema);
228
+ form.f.passengers.push({ fullName: "Ada Lovelace", infant: false });
229
+ // z.array() → typed field array; .min(1) → array-level validator gating submit
230
+ ```
231
+
232
+ `zod` is an _optional_ peer (`>=3.25`, Zod 4 supported): apps that don't
233
+ use schemas never download it.
234
+
235
+ ## The same app, four frameworks
236
+
237
+ The engine is identical everywhere — only the reactive binding and the
238
+ rendering idiom change. A complete checkout form (nested groups, typed
239
+ array rows, cancellable server validation, submit with server errors) is
240
+ implemented end-to-end, side by side, in:
241
+
242
+ - [Angular](docs/examples/angular.md) — `mdyForm` + the UI catalog, `@for` over `rows()`
243
+ - [React](docs/examples/react.md) — `useMdyForm` / `useMdyField`, array rows with `.map()`
244
+ - [Vue](docs/examples/vue.md) — `createVueForm` on `@vue/reactivity`
245
+ - [Lit](docs/examples/lit.md) — `createLitForm` + `<mdy-*-field>` custom elements
246
+
247
+ Adapter recipes, the four-primitive reactive contract and the Astro note:
248
+ [Multi-framework architecture](docs/guides/multi-framework.md).
249
+
250
+ ## Why not Reactive Forms?
251
+
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).
264
+
265
+ ## Layers
266
+
267
+ ```text
268
+ Typed API (mdyForm) Declarative API Dynamic JSON config
269
+ \ | /
270
+ Shared Form Engine (@modyra/core)
271
+ |
272
+ Headless widget layer (@modyra/widgets)
273
+ |
274
+ Angular ─ React ─ Vue ─ Lit (one native binding each)
275
+ |
276
+ Headless integrations or UI catalogs + @modyra/styles themes
277
+ ```
278
+
279
+ The form engine — typed field trees, arrays, validation, drafts, undo/redo —
280
+ lives in [`@modyra/core`](packages/core), a zero-dependency package that
281
+ runs in plain Node. [`@modyra/widgets`](packages/widgets) adds headless
282
+ widget controllers and the interaction/accessibility contract shared by
283
+ every renderer. Each adapter implements the same four-primitive reactive
284
+ contract on its framework's native reactivity: Angular signals,
285
+ `@vue/reactivity`, React's `useSyncExternalStore`, Lit's ReactiveController.
286
+
287
+ ## Angular entry points
288
+
289
+ | Import | Contents | Extra peer deps |
290
+ | :------------------------ | :---------------------------------------- | :------------------------------- |
291
+ | `@modyra/angular` | Full bundle: adapter + UI + tools | — |
292
+ | `@modyra/angular/adapter` | Headless Angular adapter layer only | — |
293
+ | `@modyra/angular/ui` | UI primitives and built-in renderers only | — |
294
+ | `@modyra/angular/zod` | `mdyFormFromSchema()` | `@modyra/zod` + `zod` (optional) |
295
+ | `@modyra/angular/interop` | `mdyCva` for Reactive Forms | `@angular/forms` (optional) |
296
+
297
+ ## Documentation
298
+
299
+ The full index lives in [docs/README.md](docs/README.md). The shortlist:
300
+
301
+ - [Mental model](docs/guides/mental-model.md) — the state graph, field lifecycle, operation semantics
302
+ - [Typed forms](docs/guides/typed-forms.md) — schema, handles, `patch`/`getChanges`, async validation, field arrays, undo/redo, **drafts (read the security note)**, wizard, Zod
303
+ - [Usage modes](docs/guides/usage-modes.md) — declarative, explicit adapter, headless, validation semantics
304
+ - [UI toolkit](docs/guides/ui-toolkit.md) — renderer catalog, enterprise select, dynamic forms, CSS tokens
305
+ - [DevTools](docs/guides/devtools.md) — hotkey overlay, masking, production notes
306
+ - [I18n](docs/guides/i18n.md) — UI strings (en/it/de/fr/es), date/time value models, localized parsing
307
+ - [Reactive Forms interop](docs/guides/interop.md) · [Comparison](docs/guides/comparison-reactive-forms.md) · [Troubleshooting](docs/guides/troubleshooting.md)
308
+
309
+ Project policies: [security](SECURITY.md) · [contributing](CONTRIBUTING.md) · [changelog](CHANGELOG.md)
310
+
311
+ ## Compatibility and status
312
+
313
+ - **Angular 21+** — the engine relies on stable signal APIs (`linkedSignal`,
314
+ `effect` semantics, signal-based inputs/queries) shipped in recent majors;
315
+ older majors are not tested and not supported.
316
+ - React ≥18, `@vue/reactivity` ≥3.4, Lit ≥3, `zod` ≥3.25 — each only for its
317
+ own adapter package.
318
+ - TypeScript strict mode; the library compiles with `strict` and
319
+ `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.
326
+
327
+ ## Examples
328
+
329
+ `examples/{react,vue,lit}` implement the **same signup form** (name +
330
+ email, shared validators, agnostic devtools panel) so the adapters can be
331
+ compared side by side, with a runtime switcher across the shipped themes
332
+ (default, Material, iOS, Ionic). They import the **built** `@modyra/*`
333
+ packages from `node_modules` — the same artifacts users install, never the
334
+ library sources.
335
+
336
+ ```bash
337
+ npm run demo:angular # Angular demo over the packaged build
338
+ npm run demo:react # http://localhost:4301
339
+ npm run demo:vue # http://localhost:4302
340
+ npm run demo:lit # http://localhost:4303
341
+ ```
342
+
343
+ ## Local development
344
+
345
+ ```bash
346
+ 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
352
+ ```
353
+
354
+ ## Brand
355
+
356
+ Logo, palette and typography live in
357
+ [`docs/assets/brand`](docs/assets/brand) — three soft modules (the
358
+ adapters) around a shared negative space (the core).
359
+
360
+ <img src="docs/assets/brand/palette.png" alt="Modyra palette — Indigo #7067FF, Violet #A855F7, Coral #FF6577, Night #0E0F16, Cloud #F8FAFC, Slate #94A3B8" width="420">
361
+
362
+ ## License
363
+
364
+ MIT © [Lorenzo Muscherà](https://github.com/lorenzomusche)
@@ -0,0 +1,19 @@
1
+ export { MDY_DATE_LOCALE, MDY_DECLARATIVE_REGISTRY, MDY_FLOATING_LABELS_DEFAULT, MDY_FLOATING_LABELS_DENSITY_DEFAULT, MDY_FORM_ADAPTER, MDY_I18N_MESSAGES, MDY_INLINE_ERRORS, MdyDeclarativeAdapter, MdyTypedForm, angularReactivity, array, field, group, mdyForm, provideModyraLocale } from '@modyra/angular';
2
+
3
+ /*
4
+ * Public API Surface of @modyra/angular/adapter.
5
+ *
6
+ * Headless Angular bindings over the framework-agnostic core engine:
7
+ * typed form model, declarative adapter, core form contracts and DI tokens.
8
+ * No renderer components and no theme assets.
9
+ *
10
+ * NOTE: this secondary entrypoint re-exports from @modyra/angular
11
+ * intentionally. ng-packagr secondary builds enforce rootDir boundaries,
12
+ * so direct imports from ../../src/... are not viable here.
13
+ */
14
+ // Typed form (mdyForm)
15
+
16
+ /**
17
+ * Generated bundle index. Do not edit.
18
+ */
19
+ //# sourceMappingURL=modyra-angular-adapter.mjs.map
@@ -0,0 +1,145 @@
1
+ import * as i0 from '@angular/core';
2
+ import { inject, Injector, HostAttributeToken, signal, effect, untracked, forwardRef, Directive } from '@angular/core';
3
+ import { NG_VALUE_ACCESSOR } from '@angular/forms';
4
+ import { MDY_FORM_ADAPTER, MdyDeclarativeAdapter, MDY_DECLARATIVE_REGISTRY } from '@modyra/angular/adapter';
5
+
6
+ /**
7
+ * Bridges any modyra renderer into classic Reactive Forms /
8
+ * `ngModel` — incremental adoption in existing codebases without an
9
+ * `<mdy-form>`:
10
+ *
11
+ * ```html
12
+ * <form [formGroup]="group">
13
+ * <mdy-control-text mdyCva name="email" formControlName="email" label="Email" />
14
+ * </form>
15
+ * ```
16
+ *
17
+ * The directive provides a private single-field adapter on the element (the
18
+ * renderer resolves it instead of a surrounding `<mdy-form>`) and implements
19
+ * `ControlValueAccessor` on top of it: `writeValue` → field value,
20
+ * user edits → `onChange`, first blur → `onTouched`, `setDisabledState` →
21
+ * field `disabled`. Validation stays in Reactive Forms — bind your own
22
+ * error display to the `FormControl`, or mirror errors into the renderer
23
+ * with `aria-*` inputs.
24
+ */
25
+ class MdyCvaDirective {
26
+ constructor() {
27
+ this.adapter = inject(MDY_FORM_ADAPTER);
28
+ this.injector = inject(Injector);
29
+ this.fieldName = inject(new HostAttributeToken("name"), { optional: true }) ?? "value";
30
+ this._disabled = signal(false, ...(ngDevMode ? [{ debugName: "_disabled" }] : /* istanbul ignore next */ []));
31
+ /** True while writeValue applies a model→view update (must not re-emit). */
32
+ this._writing = false;
33
+ this._onChange = () => undefined;
34
+ this._onTouched = () => undefined;
35
+ if (this.fieldName === "value" &&
36
+ typeof ngDevMode !== "undefined" &&
37
+ ngDevMode) {
38
+ console.warn('[modyra] mdyCva host has no "name" attribute — using the fallback field name "value".');
39
+ }
40
+ const state = this._state();
41
+ this.adapter.setDisabled(this.fieldName, this._disabled.asReadonly());
42
+ // View → model: propagate user edits, skipping writeValue echoes and
43
+ // the first run — emitting the initial value would mark the FormControl
44
+ // dirty at startup, violating the CVA contract (R21).
45
+ let first = true;
46
+ effect(() => {
47
+ const value = state.value();
48
+ untracked(() => {
49
+ if (first) {
50
+ first = false;
51
+ return;
52
+ }
53
+ if (this._writing)
54
+ return;
55
+ this._onChange(value);
56
+ });
57
+ }, { injector: this.injector });
58
+ // First touch propagates the blur to Reactive Forms.
59
+ effect(() => {
60
+ const touched = state.touched();
61
+ untracked(() => {
62
+ if (touched)
63
+ this._onTouched();
64
+ });
65
+ }, { injector: this.injector });
66
+ }
67
+ _state() {
68
+ const ref = this.adapter.getField(this.fieldName);
69
+ if (!ref) {
70
+ throw new Error(`[modyra] mdyCva could not create field "${this.fieldName}"`);
71
+ }
72
+ return ref();
73
+ }
74
+ writeValue(value) {
75
+ this._writing = true;
76
+ try {
77
+ this._state().value.set(value ?? null);
78
+ }
79
+ finally {
80
+ this._writing = false;
81
+ }
82
+ }
83
+ registerOnChange(fn) {
84
+ this._onChange = fn;
85
+ }
86
+ registerOnTouched(fn) {
87
+ this._onTouched = fn;
88
+ }
89
+ setDisabledState(isDisabled) {
90
+ this._disabled.set(isDisabled);
91
+ }
92
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: MdyCvaDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
93
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.18", type: MdyCvaDirective, isStandalone: true, selector: "[mdyCva]", providers: [
94
+ {
95
+ provide: MDY_FORM_ADAPTER,
96
+ useFactory: () => new MdyDeclarativeAdapter(signal(undefined), signal("manual")),
97
+ },
98
+ {
99
+ provide: MDY_DECLARATIVE_REGISTRY,
100
+ useExisting: MDY_FORM_ADAPTER,
101
+ },
102
+ {
103
+ provide: NG_VALUE_ACCESSOR,
104
+ useExisting: forwardRef(() => MdyCvaDirective),
105
+ multi: true,
106
+ },
107
+ ], ngImport: i0 }); }
108
+ }
109
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.18", ngImport: i0, type: MdyCvaDirective, decorators: [{
110
+ type: Directive,
111
+ args: [{
112
+ selector: "[mdyCva]",
113
+ standalone: true,
114
+ providers: [
115
+ {
116
+ provide: MDY_FORM_ADAPTER,
117
+ useFactory: () => new MdyDeclarativeAdapter(signal(undefined), signal("manual")),
118
+ },
119
+ {
120
+ provide: MDY_DECLARATIVE_REGISTRY,
121
+ useExisting: MDY_FORM_ADAPTER,
122
+ },
123
+ {
124
+ provide: NG_VALUE_ACCESSOR,
125
+ useExisting: forwardRef(() => MdyCvaDirective),
126
+ multi: true,
127
+ },
128
+ ],
129
+ }]
130
+ }], ctorParameters: () => [] });
131
+
132
+ /*
133
+ * Public API Surface of @modyra/angular/interop
134
+ *
135
+ * Interop with classic @angular/forms: the `mdyCva` directive makes every
136
+ * modyra renderer usable inside Reactive Forms / ngModel for
137
+ * incremental adoption. Requires the optional `@angular/forms` peer.
138
+ */
139
+
140
+ /**
141
+ * Generated bundle index. Do not edit.
142
+ */
143
+
144
+ export { MdyCvaDirective };
145
+ //# sourceMappingURL=modyra-angular-interop.mjs.map
@@ -0,0 +1,18 @@
1
+ export { MDY_DEVTOOLS_HOTKEY, MDY_FLOATING_LABELS, MdyA11yAnnouncer, MdyBaseControl, MdyCalendarCellComponent, MdyCalendarComponent, MdyCalendarGridComponent, MdyCalendarHeaderComponent, MdyCheckboxComponent, MdyChipsDirective, MdyColorsComponent, MdyConditionalOptionsDirective, MdyControlComponent, MdyControlLabelComponent, MdyDatePickerComponent, MdyDateRangePickerComponent, MdyDevtoolsDirective, MdyDisabledDirective, MdyDynamicFormComponent, MdyEmailDirective, MdyErrorListComponent, MdyFileComponent, MdyFloatingLabelsDirective, MdyFormArrayComponent, MdyFormComponent, MdyFormWizardComponent, MdyFormsDevtoolsComponent, MdyFormsDevtoolsOverlayComponent, MdyFormsDevtoolsService, MdyGlassDirective, MdyIconComponent, MdyInlineErrorIconComponent, MdyInlineErrorsDirective, MdyLoadOptionsDirective, MdyMaxDirective, MdyMaxLengthDirective, MdyMinDirective, MdyMinLengthDirective, MdyMultiselectComponent, MdyNumberComponent, MdyOptionDirective, MdyOptionsAutoLoadingDirective, MdyOptionsOverlayControl, MdyOverlayControl, MdyPatternDirective, MdyPrefixDirective, MdyRadioGroupComponent, MdyRangeCalendarComponent, MdyRangeCalendarGridComponent, MdyRequiredDirective, MdySegmentedButtonComponent, MdySelectComponent, MdySliderComponent, MdySuffixDirective, MdySupportingTextDirective, MdyTextComponent, MdyTextareaComponent, MdyTimepickerComponent, MdyToggleComponent, MdyValidatorBaseDirective, MdyWizardStepComponent } from '@modyra/angular';
2
+
3
+ /*
4
+ * Public API Surface of @modyra/angular/ui.
5
+ *
6
+ * Angular UI primitives and built-in renderers for modyra forms.
7
+ * This entry point intentionally excludes headless adapter contracts.
8
+ *
9
+ * NOTE: this secondary entrypoint re-exports from @modyra/angular
10
+ * intentionally. ng-packagr secondary builds enforce rootDir boundaries,
11
+ * so direct imports from ../../src/... are not viable here.
12
+ */
13
+ // Form containers
14
+
15
+ /**
16
+ * Generated bundle index. Do not edit.
17
+ */
18
+ //# sourceMappingURL=modyra-angular-ui.mjs.map
@@ -0,0 +1,33 @@
1
+ import { mdyForm } from '@modyra/angular/adapter';
2
+ import { buildZodTree, buildZodRefinementValidator } from '@modyra/zod';
3
+
4
+ /**
5
+ * Builds an Angular typed form from a `z.object()` schema — one source of
6
+ * truth for TypeScript types, validators, messages and required flags.
7
+ * See `@modyra/zod` for the shared semantics; bind the result with
8
+ * `[form]` and `[field]` exactly like `mdyForm()`.
9
+ */
10
+ function mdyFormFromSchema(schema, options) {
11
+ const tree = buildZodTree(schema);
12
+ const refinementValidator = buildZodRefinementValidator(schema);
13
+ return mdyForm(tree, {
14
+ ...(options?.submitMode !== undefined && { submitMode: options.submitMode }),
15
+ ...(options?.injector !== undefined && { injector: options.injector }),
16
+ validators: [refinementValidator, ...(options?.validators ?? [])],
17
+ });
18
+ }
19
+
20
+ /*
21
+ * Public API Surface of @modyra/angular/zod
22
+ *
23
+ * Optional Zod adapter: derive a fully typed MdyTypedForm from a z.object()
24
+ * schema — types, validators, required flags and cross-field refinements
25
+ * from a single source of truth. Requires the `zod` peer (>= 3.25).
26
+ */
27
+
28
+ /**
29
+ * Generated bundle index. Do not edit.
30
+ */
31
+
32
+ export { mdyFormFromSchema };
33
+ //# sourceMappingURL=modyra-angular-zod.mjs.map