@mosaicoo/form-angular 0.3.0 → 0.5.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 +15 -0
- package/fesm2022/mosaicoo-form-angular-designer.mjs +110 -11
- package/fesm2022/mosaicoo-form-angular-designer.mjs.map +1 -1
- package/fesm2022/mosaicoo-form-angular-material.mjs +393 -0
- package/fesm2022/mosaicoo-form-angular-material.mjs.map +1 -0
- package/fesm2022/mosaicoo-form-angular.mjs +358 -92
- package/fesm2022/mosaicoo-form-angular.mjs.map +1 -1
- package/package.json +17 -3
- package/types/mosaicoo-form-angular-designer.d.ts +8 -1
- package/types/mosaicoo-form-angular-material.d.ts +77 -0
- package/types/mosaicoo-form-angular.d.ts +139 -67
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { NgComponentOutlet, DOCUMENT } from '@angular/common';
|
|
2
2
|
import * as i0 from '@angular/core';
|
|
3
|
-
import { InjectionToken, makeEnvironmentProviders, input, signal, inject, effect, untracked, computed, ViewEncapsulation, ChangeDetectionStrategy, Component, output } from '@angular/core';
|
|
4
|
-
import { importForm, createLegacyImporter, createFormEngine, collectInputs } from '@mosaicoo/form-core';
|
|
3
|
+
import { InjectionToken, makeEnvironmentProviders, input, signal, inject, ChangeDetectorRef, reflectComponentType, effect, untracked, computed, ViewEncapsulation, ChangeDetectionStrategy, Component, output } from '@angular/core';
|
|
4
|
+
import { maskValue, importForm, createLegacyImporter, createFormEngine, collectInputs } from '@mosaicoo/form-core';
|
|
5
5
|
|
|
6
6
|
/** Multi-provider token: maps merge, later providers win on conflicts. */
|
|
7
7
|
const MFORM_FIELD_COMPONENTS = new InjectionToken('MFORM_FIELD_COMPONENTS');
|
|
@@ -11,6 +11,7 @@ const MFORM_FIELD_COMPONENTS = new InjectionToken('MFORM_FIELD_COMPONENTS');
|
|
|
11
11
|
* base URLs, caching). Without it, a plain `fetch` is used.
|
|
12
12
|
*/
|
|
13
13
|
const MFORM_REMOTE_FETCHER = new InjectionToken('MFORM_REMOTE_FETCHER');
|
|
14
|
+
const MFORM_UPLOAD = new InjectionToken('MFORM_UPLOAD');
|
|
14
15
|
/**
|
|
15
16
|
* Registers renderer extensions at any injector level:
|
|
16
17
|
*
|
|
@@ -33,6 +34,9 @@ function provideMosaicooForm(config) {
|
|
|
33
34
|
if (config.remoteFetcher) {
|
|
34
35
|
providers.push({ provide: MFORM_REMOTE_FETCHER, useValue: config.remoteFetcher });
|
|
35
36
|
}
|
|
37
|
+
if (config.uploadFiles) {
|
|
38
|
+
providers.push({ provide: MFORM_UPLOAD, useValue: config.uploadFiles });
|
|
39
|
+
}
|
|
36
40
|
return makeEnvironmentProviders(providers);
|
|
37
41
|
}
|
|
38
42
|
|
|
@@ -53,11 +57,36 @@ class FormNodeComponent {
|
|
|
53
57
|
bare = input(false, ...(ngDevMode ? [{ debugName: "bare" }] : /* istanbul ignore next */ []));
|
|
54
58
|
/** Host-registered providers for `optionsSource: provider` fields. */
|
|
55
59
|
providers = input({}, ...(ngDevMode ? [{ debugName: "providers" }] : /* istanbul ignore next */ []));
|
|
60
|
+
/** Rendering mode inherited from the renderer. */
|
|
61
|
+
mode = input('edit', ...(ngDevMode ? [{ debugName: "mode" }] : /* istanbul ignore next */ []));
|
|
62
|
+
/** Renderer-supplied refresh hook for async completions (zoneless-safe). */
|
|
63
|
+
notify = input(() => { }, ...(ngDevMode ? [{ debugName: "notify" }] : /* istanbul ignore next */ []));
|
|
56
64
|
activeTab = signal(0, ...(ngDevMode ? [{ debugName: "activeTab" }] : /* istanbul ignore next */ []));
|
|
57
65
|
/** DI-registered custom field components (maps merge; later wins). */
|
|
58
66
|
componentMaps = inject(MFORM_FIELD_COMPONENTS, { optional: true });
|
|
59
67
|
/** Host transport for remote options; plain fetch when absent. */
|
|
60
68
|
remoteFetcher = inject(MFORM_REMOTE_FETCHER, { optional: true });
|
|
69
|
+
/** Host-owned upload for `file` fields. */
|
|
70
|
+
uploader = inject(MFORM_UPLOAD, { optional: true });
|
|
71
|
+
/**
|
|
72
|
+
* Async resolutions (debounce timers, provider promises) finish outside
|
|
73
|
+
* any template event — explicitly schedule the refresh for zoneless apps.
|
|
74
|
+
*/
|
|
75
|
+
changeDetector = inject(ChangeDetectorRef);
|
|
76
|
+
uploading = signal(false, ...(ngDevMode ? [{ debugName: "uploading" }] : /* istanbul ignore next */ []));
|
|
77
|
+
/** Effective disabled state: mode, schema flag or calculated field. */
|
|
78
|
+
controlDisabled() {
|
|
79
|
+
return (this.mode() !== 'edit' ||
|
|
80
|
+
this.field().disabled === true ||
|
|
81
|
+
this.field().calculate !== undefined);
|
|
82
|
+
}
|
|
83
|
+
interactive() {
|
|
84
|
+
return this.mode() === 'edit';
|
|
85
|
+
}
|
|
86
|
+
pending() {
|
|
87
|
+
this.tick();
|
|
88
|
+
return this.engine().isPending(this.path());
|
|
89
|
+
}
|
|
61
90
|
/** Custom component registered for this field type, if any. */
|
|
62
91
|
customComponent() {
|
|
63
92
|
const node = this.node();
|
|
@@ -70,14 +99,22 @@ class FormNodeComponent {
|
|
|
70
99
|
}
|
|
71
100
|
return null;
|
|
72
101
|
}
|
|
73
|
-
/**
|
|
102
|
+
/**
|
|
103
|
+
* Inputs handed to a custom component (MformFieldComponent contract).
|
|
104
|
+
* Only inputs the component actually declares are passed, so optional
|
|
105
|
+
* contract members (`mode`) never break third-party components.
|
|
106
|
+
*/
|
|
74
107
|
customInputs() {
|
|
75
|
-
|
|
108
|
+
const component = this.customComponent();
|
|
109
|
+
const declared = new Set(component ? (reflectComponentType(component)?.inputs ?? []).map((i) => i.propName) : []);
|
|
110
|
+
const all = {
|
|
76
111
|
field: this.field(),
|
|
77
112
|
engine: this.engine(),
|
|
78
113
|
path: this.path(),
|
|
79
114
|
tick: this.tick(),
|
|
115
|
+
mode: this.mode(),
|
|
80
116
|
};
|
|
117
|
+
return Object.fromEntries(Object.entries(all).filter(([key]) => declared.has(key)));
|
|
81
118
|
}
|
|
82
119
|
/** Options resolved from a dynamic source (provider or remote URL). */
|
|
83
120
|
resolvedOptions = signal(null, ...(ngDevMode ? [{ debugName: "resolvedOptions" }] : /* istanbul ignore next */ []));
|
|
@@ -119,7 +156,11 @@ class FormNodeComponent {
|
|
|
119
156
|
if (!provider)
|
|
120
157
|
return; // no provider: static options/fallback stand
|
|
121
158
|
Promise.resolve(provider({ field: node, path, data: engine.getData(), args: source.args }))
|
|
122
|
-
.then(
|
|
159
|
+
.then((options) => {
|
|
160
|
+
apply(options);
|
|
161
|
+
this.changeDetector.markForCheck();
|
|
162
|
+
this.notify()();
|
|
163
|
+
})
|
|
123
164
|
.catch(() => { }); // provider failure keeps the static fallback
|
|
124
165
|
}
|
|
125
166
|
else {
|
|
@@ -133,6 +174,7 @@ class FormNodeComponent {
|
|
|
133
174
|
label: String(item[source.labelKey ?? 'label'] ?? item[source.valueKey ?? 'value'] ?? ''),
|
|
134
175
|
value: (item[source.valueKey ?? 'value'] ?? ''),
|
|
135
176
|
}))))
|
|
177
|
+
.then(() => this.changeDetector.markForCheck())
|
|
136
178
|
.catch(() => { });
|
|
137
179
|
}
|
|
138
180
|
});
|
|
@@ -217,7 +259,12 @@ class FormNodeComponent {
|
|
|
217
259
|
}
|
|
218
260
|
stringValue() {
|
|
219
261
|
const value = this.value();
|
|
220
|
-
|
|
262
|
+
if (value === undefined || value === null)
|
|
263
|
+
return '';
|
|
264
|
+
const mask = this.field().format?.mask;
|
|
265
|
+
if (mask && typeof value === 'string')
|
|
266
|
+
return maskValue(value, mask).masked;
|
|
267
|
+
return String(value);
|
|
221
268
|
}
|
|
222
269
|
mapValue() {
|
|
223
270
|
const value = this.value();
|
|
@@ -275,7 +322,15 @@ class FormNodeComponent {
|
|
|
275
322
|
this.engine().setValue(this.path(), value);
|
|
276
323
|
}
|
|
277
324
|
setString(event) {
|
|
278
|
-
const
|
|
325
|
+
const target = event.target;
|
|
326
|
+
const raw = target.value;
|
|
327
|
+
const mask = this.field().format?.mask;
|
|
328
|
+
if (mask) {
|
|
329
|
+
const { masked, raw: stripped } = maskValue(raw, mask);
|
|
330
|
+
target.value = masked; // the control always shows the masked shape
|
|
331
|
+
this.setValue(this.field().format?.keepMask ? masked : stripped);
|
|
332
|
+
return;
|
|
333
|
+
}
|
|
279
334
|
if (this.field().dataType === 'number') {
|
|
280
335
|
const parsed = raw === '' ? undefined : Number(raw);
|
|
281
336
|
this.setValue(parsed !== undefined && Number.isFinite(parsed) ? parsed : undefined);
|
|
@@ -296,7 +351,32 @@ class FormNodeComponent {
|
|
|
296
351
|
}
|
|
297
352
|
setFiles(event) {
|
|
298
353
|
const files = event.target.files;
|
|
299
|
-
|
|
354
|
+
const list = files ? Array.from(files) : [];
|
|
355
|
+
if (!this.uploader || list.length === 0) {
|
|
356
|
+
// no host uploader: only file names are stored
|
|
357
|
+
this.setValue(list.map((f) => f.name));
|
|
358
|
+
return;
|
|
359
|
+
}
|
|
360
|
+
const field = this.field();
|
|
361
|
+
const path = this.path();
|
|
362
|
+
this.uploading.set(true);
|
|
363
|
+
Promise.all(list.map((file) => this.uploader(file, { field, path })))
|
|
364
|
+
.then((refs) => this.setValue(refs))
|
|
365
|
+
.catch(() => { }) // host upload failure keeps the previous value
|
|
366
|
+
.finally(() => {
|
|
367
|
+
this.uploading.set(false);
|
|
368
|
+
this.changeDetector.markForCheck();
|
|
369
|
+
this.notify()();
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
/** Names shown under a `file` control (refs or plain names). */
|
|
373
|
+
fileNames() {
|
|
374
|
+
const value = this.value();
|
|
375
|
+
if (!Array.isArray(value))
|
|
376
|
+
return [];
|
|
377
|
+
return value.map((item) => typeof item === 'object' && item !== null
|
|
378
|
+
? String(item.name ?? '')
|
|
379
|
+
: String(item));
|
|
300
380
|
}
|
|
301
381
|
prefix() {
|
|
302
382
|
const value = this.node().props?.['prefix'];
|
|
@@ -326,7 +406,69 @@ class FormNodeComponent {
|
|
|
326
406
|
}
|
|
327
407
|
touch() {
|
|
328
408
|
this.engine().markTouched(this.path());
|
|
329
|
-
|
|
409
|
+
// Async-aware: resolves immediately for sync-only fields; pending events
|
|
410
|
+
// drive the busy indicator otherwise.
|
|
411
|
+
void this.engine().validateFieldAsync(this.path());
|
|
412
|
+
}
|
|
413
|
+
// -- reference (autocomplete) ---------------------------------------------
|
|
414
|
+
refOptions = signal([], ...(ngDevMode ? [{ debugName: "refOptions" }] : /* istanbul ignore next */ []));
|
|
415
|
+
refOpen = signal(false, ...(ngDevMode ? [{ debugName: "refOpen" }] : /* istanbul ignore next */ []));
|
|
416
|
+
refTimer = null;
|
|
417
|
+
/** Label of the chosen option (falls back to the raw value). */
|
|
418
|
+
refLabel = signal(null, ...(ngDevMode ? [{ debugName: "refLabel" }] : /* istanbul ignore next */ []));
|
|
419
|
+
refDisplay() {
|
|
420
|
+
return this.refLabel() ?? this.stringValue();
|
|
421
|
+
}
|
|
422
|
+
refSearch(event) {
|
|
423
|
+
const query = event.target.value;
|
|
424
|
+
this.refLabel.set(null);
|
|
425
|
+
if (this.refTimer)
|
|
426
|
+
clearTimeout(this.refTimer);
|
|
427
|
+
this.refTimer = setTimeout(() => this.refResolve(query), 250);
|
|
428
|
+
}
|
|
429
|
+
refResolve(query) {
|
|
430
|
+
const source = this.field().optionsSource;
|
|
431
|
+
if (!source || source.type !== 'provider') {
|
|
432
|
+
// static options filter locally
|
|
433
|
+
const filter = query.toLowerCase();
|
|
434
|
+
this.refOptions.set((this.field().options ?? []).filter((o) => o.label.toLowerCase().includes(filter)));
|
|
435
|
+
this.refOpen.set(true);
|
|
436
|
+
this.changeDetector.markForCheck();
|
|
437
|
+
this.notify()();
|
|
438
|
+
return;
|
|
439
|
+
}
|
|
440
|
+
const provider = this.providers()[source.name];
|
|
441
|
+
if (!provider)
|
|
442
|
+
return;
|
|
443
|
+
Promise.resolve(provider({
|
|
444
|
+
field: this.field(),
|
|
445
|
+
path: this.path(),
|
|
446
|
+
data: this.engine().getData(),
|
|
447
|
+
args: source.args,
|
|
448
|
+
query,
|
|
449
|
+
}))
|
|
450
|
+
.then((options) => {
|
|
451
|
+
this.refOptions.set(options);
|
|
452
|
+
this.refOpen.set(true);
|
|
453
|
+
this.changeDetector.markForCheck();
|
|
454
|
+
this.notify()();
|
|
455
|
+
})
|
|
456
|
+
.catch(() => { });
|
|
457
|
+
}
|
|
458
|
+
refPick(option) {
|
|
459
|
+
this.setValue(option.value);
|
|
460
|
+
this.refLabel.set(option.label);
|
|
461
|
+
this.refOpen.set(false);
|
|
462
|
+
this.touch();
|
|
463
|
+
}
|
|
464
|
+
refBlur() {
|
|
465
|
+
// let a click on an option land before closing
|
|
466
|
+
setTimeout(() => {
|
|
467
|
+
this.refOpen.set(false);
|
|
468
|
+
this.changeDetector.markForCheck();
|
|
469
|
+
this.notify()();
|
|
470
|
+
}, 150);
|
|
471
|
+
this.touch();
|
|
330
472
|
}
|
|
331
473
|
addRow() {
|
|
332
474
|
this.engine().addRow(this.path());
|
|
@@ -335,18 +477,20 @@ class FormNodeComponent {
|
|
|
335
477
|
this.engine().removeRow(this.path(), index);
|
|
336
478
|
}
|
|
337
479
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: FormNodeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
338
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.19", type: FormNodeComponent, isStandalone: true, selector: "mform-node", inputs: { node: { classPropertyName: "node", publicName: "node", isSignal: true, isRequired: true, transformFunction: null }, engine: { classPropertyName: "engine", publicName: "engine", isSignal: true, isRequired: true, transformFunction: null }, tick: { classPropertyName: "tick", publicName: "tick", isSignal: true, isRequired: true, transformFunction: null }, scope: { classPropertyName: "scope", publicName: "scope", isSignal: true, isRequired: false, transformFunction: null }, bare: { classPropertyName: "bare", publicName: "bare", isSignal: true, isRequired: false, transformFunction: null }, providers: { classPropertyName: "providers", publicName: "providers", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "mform-node" }, ngImport: i0, template: `
|
|
480
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.19", type: FormNodeComponent, isStandalone: true, selector: "mform-node", inputs: { node: { classPropertyName: "node", publicName: "node", isSignal: true, isRequired: true, transformFunction: null }, engine: { classPropertyName: "engine", publicName: "engine", isSignal: true, isRequired: true, transformFunction: null }, tick: { classPropertyName: "tick", publicName: "tick", isSignal: true, isRequired: true, transformFunction: null }, scope: { classPropertyName: "scope", publicName: "scope", isSignal: true, isRequired: false, transformFunction: null }, bare: { classPropertyName: "bare", publicName: "bare", isSignal: true, isRequired: false, transformFunction: null }, providers: { classPropertyName: "providers", publicName: "providers", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, notify: { classPropertyName: "notify", publicName: "notify", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "mform-node" }, ngImport: i0, template: `
|
|
339
481
|
@let n = node();
|
|
340
482
|
@if (visible()) {
|
|
341
483
|
@switch (n.kind) {
|
|
342
484
|
@case ('static') {
|
|
343
485
|
@if (n.type === 'button') {
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
486
|
+
@if (interactive()) {
|
|
487
|
+
<button
|
|
488
|
+
class="mform-btn"
|
|
489
|
+
[type]="buttonAction() === 'submit' ? 'submit' : 'button'"
|
|
490
|
+
>
|
|
491
|
+
{{ n.label || 'Submit' }}
|
|
492
|
+
</button>
|
|
493
|
+
}
|
|
350
494
|
} @else if (n.type === 'content') {
|
|
351
495
|
<div class="mform-content" [innerHTML]="content()"></div>
|
|
352
496
|
} @else {
|
|
@@ -362,7 +506,7 @@ class FormNodeComponent {
|
|
|
362
506
|
@for (column of columns(); track $index) {
|
|
363
507
|
<div class="mform-column">
|
|
364
508
|
@for (child of column; track child.key) {
|
|
365
|
-
<mform-node [node]="child" [engine]="engine()" [tick]="tick()" [providers]="providers()" [scope]="childScope()" />
|
|
509
|
+
<mform-node [node]="child" [engine]="engine()" [tick]="tick()" [providers]="providers()" [mode]="mode()" [notify]="notify()" [scope]="childScope()" />
|
|
366
510
|
}
|
|
367
511
|
</div>
|
|
368
512
|
}
|
|
@@ -387,7 +531,7 @@ class FormNodeComponent {
|
|
|
387
531
|
@for (tab of container().children; track tab.key; let i = $index) {
|
|
388
532
|
@if (activeTab() === i) {
|
|
389
533
|
<div class="mform-tabpanel" role="tabpanel">
|
|
390
|
-
<mform-node [node]="tab" [engine]="engine()" [tick]="tick()" [providers]="providers()" [scope]="childScope()" [bare]="true" />
|
|
534
|
+
<mform-node [node]="tab" [engine]="engine()" [tick]="tick()" [providers]="providers()" [mode]="mode()" [notify]="notify()" [scope]="childScope()" [bare]="true" />
|
|
391
535
|
</div>
|
|
392
536
|
}
|
|
393
537
|
}
|
|
@@ -403,20 +547,24 @@ class FormNodeComponent {
|
|
|
403
547
|
<div class="mform-grid-row" role="group" [attr.aria-label]="(container().label || container().key) + ' ' + (i + 1)">
|
|
404
548
|
<div class="mform-grid-fields">
|
|
405
549
|
@for (child of container().children; track child.key) {
|
|
406
|
-
<mform-node [node]="child" [engine]="engine()" [tick]="tick()" [providers]="providers()" [scope]="rowScope(i)" />
|
|
550
|
+
<mform-node [node]="child" [engine]="engine()" [tick]="tick()" [providers]="providers()" [mode]="mode()" [notify]="notify()" [scope]="rowScope(i)" />
|
|
407
551
|
}
|
|
408
552
|
</div>
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
553
|
+
@if (interactive()) {
|
|
554
|
+
<button
|
|
555
|
+
type="button"
|
|
556
|
+
class="mform-row-remove"
|
|
557
|
+
(click)="removeRow(i)"
|
|
558
|
+
[attr.aria-label]="'Remove row ' + (i + 1)"
|
|
559
|
+
>
|
|
560
|
+
✕
|
|
561
|
+
</button>
|
|
562
|
+
}
|
|
417
563
|
</div>
|
|
418
564
|
}
|
|
419
|
-
|
|
565
|
+
@if (interactive()) {
|
|
566
|
+
<button type="button" class="mform-row-add" (click)="addRow()">+ Add</button>
|
|
567
|
+
}
|
|
420
568
|
</div>
|
|
421
569
|
</section>
|
|
422
570
|
}
|
|
@@ -426,14 +574,14 @@ class FormNodeComponent {
|
|
|
426
574
|
<header class="mform-panel-head">{{ container().label }}</header>
|
|
427
575
|
<div class="mform-panel-body">
|
|
428
576
|
@for (child of container().children; track child.key) {
|
|
429
|
-
<mform-node [node]="child" [engine]="engine()" [tick]="tick()" [providers]="providers()" [scope]="childScope()" />
|
|
577
|
+
<mform-node [node]="child" [engine]="engine()" [tick]="tick()" [providers]="providers()" [mode]="mode()" [notify]="notify()" [scope]="childScope()" />
|
|
430
578
|
}
|
|
431
579
|
</div>
|
|
432
580
|
</section>
|
|
433
581
|
} @else {
|
|
434
582
|
<div class="mform-group">
|
|
435
583
|
@for (child of container().children; track child.key) {
|
|
436
|
-
<mform-node [node]="child" [engine]="engine()" [tick]="tick()" [providers]="providers()" [scope]="childScope()" />
|
|
584
|
+
<mform-node [node]="child" [engine]="engine()" [tick]="tick()" [providers]="providers()" [mode]="mode()" [notify]="notify()" [scope]="childScope()" />
|
|
437
585
|
}
|
|
438
586
|
</div>
|
|
439
587
|
}
|
|
@@ -447,7 +595,7 @@ class FormNodeComponent {
|
|
|
447
595
|
[ngComponentOutletInputs]="customInputs()"
|
|
448
596
|
/>
|
|
449
597
|
} @else if (field().type !== 'hidden') {
|
|
450
|
-
<div class="mform-field" [class.has-error]="errors().length > 0">
|
|
598
|
+
<div class="mform-field" [class.has-error]="errors().length > 0" [class.is-pending]="pending()">
|
|
451
599
|
@if (field().type !== 'checkbox') {
|
|
452
600
|
<label class="mform-label" [attr.for]="controlId()">
|
|
453
601
|
{{ field().label || field().key }}
|
|
@@ -463,7 +611,7 @@ class FormNodeComponent {
|
|
|
463
611
|
[id]="controlId()"
|
|
464
612
|
[value]="stringValue()"
|
|
465
613
|
[placeholder]="field().placeholder || ''"
|
|
466
|
-
[disabled]="
|
|
614
|
+
[disabled]="controlDisabled()"
|
|
467
615
|
[attr.rows]="rowsProp()"
|
|
468
616
|
[attr.aria-required]="required() || null"
|
|
469
617
|
[attr.aria-invalid]="errors().length > 0 || null"
|
|
@@ -476,7 +624,7 @@ class FormNodeComponent {
|
|
|
476
624
|
<select
|
|
477
625
|
class="mform-control"
|
|
478
626
|
[id]="controlId()"
|
|
479
|
-
[disabled]="
|
|
627
|
+
[disabled]="controlDisabled()"
|
|
480
628
|
[attr.aria-required]="required() || null"
|
|
481
629
|
[attr.aria-invalid]="errors().length > 0 || null"
|
|
482
630
|
[attr.aria-describedby]="describedBy()"
|
|
@@ -502,7 +650,7 @@ class FormNodeComponent {
|
|
|
502
650
|
type="radio"
|
|
503
651
|
[name]="controlId()"
|
|
504
652
|
[checked]="stringValue() === '' + option.value"
|
|
505
|
-
[disabled]="
|
|
653
|
+
[disabled]="controlDisabled()"
|
|
506
654
|
(change)="setValue(option.value)"
|
|
507
655
|
(blur)="touch()"
|
|
508
656
|
/>
|
|
@@ -517,7 +665,7 @@ class FormNodeComponent {
|
|
|
517
665
|
type="checkbox"
|
|
518
666
|
[id]="controlId()"
|
|
519
667
|
[checked]="value() === true"
|
|
520
|
-
[disabled]="
|
|
668
|
+
[disabled]="controlDisabled()"
|
|
521
669
|
[attr.aria-invalid]="errors().length > 0 || null"
|
|
522
670
|
[attr.aria-describedby]="describedBy()"
|
|
523
671
|
(change)="setChecked($event)"
|
|
@@ -538,7 +686,7 @@ class FormNodeComponent {
|
|
|
538
686
|
<input
|
|
539
687
|
type="checkbox"
|
|
540
688
|
[checked]="mapValue()['' + option.value] === true"
|
|
541
|
-
[disabled]="
|
|
689
|
+
[disabled]="controlDisabled()"
|
|
542
690
|
(change)="toggleMap('' + option.value, $event)"
|
|
543
691
|
(blur)="touch()"
|
|
544
692
|
/>
|
|
@@ -564,7 +712,7 @@ class FormNodeComponent {
|
|
|
564
712
|
type="text"
|
|
565
713
|
[id]="controlId()"
|
|
566
714
|
[placeholder]="field().placeholder || ''"
|
|
567
|
-
[disabled]="
|
|
715
|
+
[disabled]="controlDisabled()"
|
|
568
716
|
[attr.aria-describedby]="describedBy()"
|
|
569
717
|
(keydown.enter)="addTag($event)"
|
|
570
718
|
(blur)="touch()"
|
|
@@ -577,11 +725,45 @@ class FormNodeComponent {
|
|
|
577
725
|
type="file"
|
|
578
726
|
multiple
|
|
579
727
|
[id]="controlId()"
|
|
580
|
-
[disabled]="
|
|
728
|
+
[disabled]="controlDisabled() || uploading()"
|
|
729
|
+
[attr.aria-busy]="uploading() || null"
|
|
581
730
|
[attr.aria-describedby]="describedBy()"
|
|
582
731
|
(change)="setFiles($event)"
|
|
583
732
|
(blur)="touch()"
|
|
584
733
|
/>
|
|
734
|
+
@if (uploading()) {
|
|
735
|
+
<div class="mform-desc" role="status">Uploading…</div>
|
|
736
|
+
}
|
|
737
|
+
@for (name of fileNames(); track $index) {
|
|
738
|
+
<div class="mform-file-name">📎 {{ name }}</div>
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
@case ('reference') {
|
|
742
|
+
<div class="mform-reference">
|
|
743
|
+
<input
|
|
744
|
+
class="mform-control"
|
|
745
|
+
type="text"
|
|
746
|
+
role="combobox"
|
|
747
|
+
[id]="controlId()"
|
|
748
|
+
[value]="refDisplay()"
|
|
749
|
+
[placeholder]="field().placeholder || ''"
|
|
750
|
+
[disabled]="controlDisabled()"
|
|
751
|
+
[attr.aria-expanded]="refOpen()"
|
|
752
|
+
[attr.aria-invalid]="errors().length > 0 || null"
|
|
753
|
+
[attr.aria-describedby]="describedBy()"
|
|
754
|
+
(input)="refSearch($event)"
|
|
755
|
+
(blur)="refBlur()"
|
|
756
|
+
/>
|
|
757
|
+
@if (refOpen() && refOptions().length > 0) {
|
|
758
|
+
<ul class="mform-ref-list" role="listbox">
|
|
759
|
+
@for (option of refOptions(); track option.value) {
|
|
760
|
+
<li role="option">
|
|
761
|
+
<button type="button" (click)="refPick(option)">{{ option.label }}</button>
|
|
762
|
+
</li>
|
|
763
|
+
}
|
|
764
|
+
</ul>
|
|
765
|
+
}
|
|
766
|
+
</div>
|
|
585
767
|
}
|
|
586
768
|
@case ('unsupported') {
|
|
587
769
|
<input
|
|
@@ -603,7 +785,7 @@ class FormNodeComponent {
|
|
|
603
785
|
[id]="controlId()"
|
|
604
786
|
[value]="stringValue()"
|
|
605
787
|
[placeholder]="field().placeholder || ''"
|
|
606
|
-
[disabled]="
|
|
788
|
+
[disabled]="controlDisabled()"
|
|
607
789
|
[attr.step]="field().type === 'currency' ? '0.01' : null"
|
|
608
790
|
[attr.inputmode]="field().type === 'currency' ? 'decimal' : null"
|
|
609
791
|
[attr.aria-required]="required() || null"
|
|
@@ -629,7 +811,7 @@ class FormNodeComponent {
|
|
|
629
811
|
}
|
|
630
812
|
}
|
|
631
813
|
}
|
|
632
|
-
`, isInline: true, dependencies: [{ kind: "component", type: FormNodeComponent, selector: "mform-node", inputs: ["node", "engine", "tick", "scope", "bare", "providers"] }, { kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
814
|
+
`, isInline: true, dependencies: [{ kind: "component", type: FormNodeComponent, selector: "mform-node", inputs: ["node", "engine", "tick", "scope", "bare", "providers", "mode", "notify"] }, { kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
633
815
|
}
|
|
634
816
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: FormNodeComponent, decorators: [{
|
|
635
817
|
type: Component,
|
|
@@ -645,12 +827,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImpo
|
|
|
645
827
|
@switch (n.kind) {
|
|
646
828
|
@case ('static') {
|
|
647
829
|
@if (n.type === 'button') {
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
830
|
+
@if (interactive()) {
|
|
831
|
+
<button
|
|
832
|
+
class="mform-btn"
|
|
833
|
+
[type]="buttonAction() === 'submit' ? 'submit' : 'button'"
|
|
834
|
+
>
|
|
835
|
+
{{ n.label || 'Submit' }}
|
|
836
|
+
</button>
|
|
837
|
+
}
|
|
654
838
|
} @else if (n.type === 'content') {
|
|
655
839
|
<div class="mform-content" [innerHTML]="content()"></div>
|
|
656
840
|
} @else {
|
|
@@ -666,7 +850,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImpo
|
|
|
666
850
|
@for (column of columns(); track $index) {
|
|
667
851
|
<div class="mform-column">
|
|
668
852
|
@for (child of column; track child.key) {
|
|
669
|
-
<mform-node [node]="child" [engine]="engine()" [tick]="tick()" [providers]="providers()" [scope]="childScope()" />
|
|
853
|
+
<mform-node [node]="child" [engine]="engine()" [tick]="tick()" [providers]="providers()" [mode]="mode()" [notify]="notify()" [scope]="childScope()" />
|
|
670
854
|
}
|
|
671
855
|
</div>
|
|
672
856
|
}
|
|
@@ -691,7 +875,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImpo
|
|
|
691
875
|
@for (tab of container().children; track tab.key; let i = $index) {
|
|
692
876
|
@if (activeTab() === i) {
|
|
693
877
|
<div class="mform-tabpanel" role="tabpanel">
|
|
694
|
-
<mform-node [node]="tab" [engine]="engine()" [tick]="tick()" [providers]="providers()" [scope]="childScope()" [bare]="true" />
|
|
878
|
+
<mform-node [node]="tab" [engine]="engine()" [tick]="tick()" [providers]="providers()" [mode]="mode()" [notify]="notify()" [scope]="childScope()" [bare]="true" />
|
|
695
879
|
</div>
|
|
696
880
|
}
|
|
697
881
|
}
|
|
@@ -707,20 +891,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImpo
|
|
|
707
891
|
<div class="mform-grid-row" role="group" [attr.aria-label]="(container().label || container().key) + ' ' + (i + 1)">
|
|
708
892
|
<div class="mform-grid-fields">
|
|
709
893
|
@for (child of container().children; track child.key) {
|
|
710
|
-
<mform-node [node]="child" [engine]="engine()" [tick]="tick()" [providers]="providers()" [scope]="rowScope(i)" />
|
|
894
|
+
<mform-node [node]="child" [engine]="engine()" [tick]="tick()" [providers]="providers()" [mode]="mode()" [notify]="notify()" [scope]="rowScope(i)" />
|
|
711
895
|
}
|
|
712
896
|
</div>
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
897
|
+
@if (interactive()) {
|
|
898
|
+
<button
|
|
899
|
+
type="button"
|
|
900
|
+
class="mform-row-remove"
|
|
901
|
+
(click)="removeRow(i)"
|
|
902
|
+
[attr.aria-label]="'Remove row ' + (i + 1)"
|
|
903
|
+
>
|
|
904
|
+
✕
|
|
905
|
+
</button>
|
|
906
|
+
}
|
|
721
907
|
</div>
|
|
722
908
|
}
|
|
723
|
-
|
|
909
|
+
@if (interactive()) {
|
|
910
|
+
<button type="button" class="mform-row-add" (click)="addRow()">+ Add</button>
|
|
911
|
+
}
|
|
724
912
|
</div>
|
|
725
913
|
</section>
|
|
726
914
|
}
|
|
@@ -730,14 +918,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImpo
|
|
|
730
918
|
<header class="mform-panel-head">{{ container().label }}</header>
|
|
731
919
|
<div class="mform-panel-body">
|
|
732
920
|
@for (child of container().children; track child.key) {
|
|
733
|
-
<mform-node [node]="child" [engine]="engine()" [tick]="tick()" [providers]="providers()" [scope]="childScope()" />
|
|
921
|
+
<mform-node [node]="child" [engine]="engine()" [tick]="tick()" [providers]="providers()" [mode]="mode()" [notify]="notify()" [scope]="childScope()" />
|
|
734
922
|
}
|
|
735
923
|
</div>
|
|
736
924
|
</section>
|
|
737
925
|
} @else {
|
|
738
926
|
<div class="mform-group">
|
|
739
927
|
@for (child of container().children; track child.key) {
|
|
740
|
-
<mform-node [node]="child" [engine]="engine()" [tick]="tick()" [providers]="providers()" [scope]="childScope()" />
|
|
928
|
+
<mform-node [node]="child" [engine]="engine()" [tick]="tick()" [providers]="providers()" [mode]="mode()" [notify]="notify()" [scope]="childScope()" />
|
|
741
929
|
}
|
|
742
930
|
</div>
|
|
743
931
|
}
|
|
@@ -751,7 +939,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImpo
|
|
|
751
939
|
[ngComponentOutletInputs]="customInputs()"
|
|
752
940
|
/>
|
|
753
941
|
} @else if (field().type !== 'hidden') {
|
|
754
|
-
<div class="mform-field" [class.has-error]="errors().length > 0">
|
|
942
|
+
<div class="mform-field" [class.has-error]="errors().length > 0" [class.is-pending]="pending()">
|
|
755
943
|
@if (field().type !== 'checkbox') {
|
|
756
944
|
<label class="mform-label" [attr.for]="controlId()">
|
|
757
945
|
{{ field().label || field().key }}
|
|
@@ -767,7 +955,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImpo
|
|
|
767
955
|
[id]="controlId()"
|
|
768
956
|
[value]="stringValue()"
|
|
769
957
|
[placeholder]="field().placeholder || ''"
|
|
770
|
-
[disabled]="
|
|
958
|
+
[disabled]="controlDisabled()"
|
|
771
959
|
[attr.rows]="rowsProp()"
|
|
772
960
|
[attr.aria-required]="required() || null"
|
|
773
961
|
[attr.aria-invalid]="errors().length > 0 || null"
|
|
@@ -780,7 +968,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImpo
|
|
|
780
968
|
<select
|
|
781
969
|
class="mform-control"
|
|
782
970
|
[id]="controlId()"
|
|
783
|
-
[disabled]="
|
|
971
|
+
[disabled]="controlDisabled()"
|
|
784
972
|
[attr.aria-required]="required() || null"
|
|
785
973
|
[attr.aria-invalid]="errors().length > 0 || null"
|
|
786
974
|
[attr.aria-describedby]="describedBy()"
|
|
@@ -806,7 +994,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImpo
|
|
|
806
994
|
type="radio"
|
|
807
995
|
[name]="controlId()"
|
|
808
996
|
[checked]="stringValue() === '' + option.value"
|
|
809
|
-
[disabled]="
|
|
997
|
+
[disabled]="controlDisabled()"
|
|
810
998
|
(change)="setValue(option.value)"
|
|
811
999
|
(blur)="touch()"
|
|
812
1000
|
/>
|
|
@@ -821,7 +1009,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImpo
|
|
|
821
1009
|
type="checkbox"
|
|
822
1010
|
[id]="controlId()"
|
|
823
1011
|
[checked]="value() === true"
|
|
824
|
-
[disabled]="
|
|
1012
|
+
[disabled]="controlDisabled()"
|
|
825
1013
|
[attr.aria-invalid]="errors().length > 0 || null"
|
|
826
1014
|
[attr.aria-describedby]="describedBy()"
|
|
827
1015
|
(change)="setChecked($event)"
|
|
@@ -842,7 +1030,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImpo
|
|
|
842
1030
|
<input
|
|
843
1031
|
type="checkbox"
|
|
844
1032
|
[checked]="mapValue()['' + option.value] === true"
|
|
845
|
-
[disabled]="
|
|
1033
|
+
[disabled]="controlDisabled()"
|
|
846
1034
|
(change)="toggleMap('' + option.value, $event)"
|
|
847
1035
|
(blur)="touch()"
|
|
848
1036
|
/>
|
|
@@ -868,7 +1056,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImpo
|
|
|
868
1056
|
type="text"
|
|
869
1057
|
[id]="controlId()"
|
|
870
1058
|
[placeholder]="field().placeholder || ''"
|
|
871
|
-
[disabled]="
|
|
1059
|
+
[disabled]="controlDisabled()"
|
|
872
1060
|
[attr.aria-describedby]="describedBy()"
|
|
873
1061
|
(keydown.enter)="addTag($event)"
|
|
874
1062
|
(blur)="touch()"
|
|
@@ -881,11 +1069,45 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImpo
|
|
|
881
1069
|
type="file"
|
|
882
1070
|
multiple
|
|
883
1071
|
[id]="controlId()"
|
|
884
|
-
[disabled]="
|
|
1072
|
+
[disabled]="controlDisabled() || uploading()"
|
|
1073
|
+
[attr.aria-busy]="uploading() || null"
|
|
885
1074
|
[attr.aria-describedby]="describedBy()"
|
|
886
1075
|
(change)="setFiles($event)"
|
|
887
1076
|
(blur)="touch()"
|
|
888
1077
|
/>
|
|
1078
|
+
@if (uploading()) {
|
|
1079
|
+
<div class="mform-desc" role="status">Uploading…</div>
|
|
1080
|
+
}
|
|
1081
|
+
@for (name of fileNames(); track $index) {
|
|
1082
|
+
<div class="mform-file-name">📎 {{ name }}</div>
|
|
1083
|
+
}
|
|
1084
|
+
}
|
|
1085
|
+
@case ('reference') {
|
|
1086
|
+
<div class="mform-reference">
|
|
1087
|
+
<input
|
|
1088
|
+
class="mform-control"
|
|
1089
|
+
type="text"
|
|
1090
|
+
role="combobox"
|
|
1091
|
+
[id]="controlId()"
|
|
1092
|
+
[value]="refDisplay()"
|
|
1093
|
+
[placeholder]="field().placeholder || ''"
|
|
1094
|
+
[disabled]="controlDisabled()"
|
|
1095
|
+
[attr.aria-expanded]="refOpen()"
|
|
1096
|
+
[attr.aria-invalid]="errors().length > 0 || null"
|
|
1097
|
+
[attr.aria-describedby]="describedBy()"
|
|
1098
|
+
(input)="refSearch($event)"
|
|
1099
|
+
(blur)="refBlur()"
|
|
1100
|
+
/>
|
|
1101
|
+
@if (refOpen() && refOptions().length > 0) {
|
|
1102
|
+
<ul class="mform-ref-list" role="listbox">
|
|
1103
|
+
@for (option of refOptions(); track option.value) {
|
|
1104
|
+
<li role="option">
|
|
1105
|
+
<button type="button" (click)="refPick(option)">{{ option.label }}</button>
|
|
1106
|
+
</li>
|
|
1107
|
+
}
|
|
1108
|
+
</ul>
|
|
1109
|
+
}
|
|
1110
|
+
</div>
|
|
889
1111
|
}
|
|
890
1112
|
@case ('unsupported') {
|
|
891
1113
|
<input
|
|
@@ -907,7 +1129,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImpo
|
|
|
907
1129
|
[id]="controlId()"
|
|
908
1130
|
[value]="stringValue()"
|
|
909
1131
|
[placeholder]="field().placeholder || ''"
|
|
910
|
-
[disabled]="
|
|
1132
|
+
[disabled]="controlDisabled()"
|
|
911
1133
|
[attr.step]="field().type === 'currency' ? '0.01' : null"
|
|
912
1134
|
[attr.inputmode]="field().type === 'currency' ? 'decimal' : null"
|
|
913
1135
|
[attr.aria-required]="required() || null"
|
|
@@ -935,7 +1157,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImpo
|
|
|
935
1157
|
}
|
|
936
1158
|
`,
|
|
937
1159
|
}]
|
|
938
|
-
}], ctorParameters: () => [], propDecorators: { node: [{ type: i0.Input, args: [{ isSignal: true, alias: "node", required: true }] }], engine: [{ type: i0.Input, args: [{ isSignal: true, alias: "engine", required: true }] }], tick: [{ type: i0.Input, args: [{ isSignal: true, alias: "tick", required: true }] }], scope: [{ type: i0.Input, args: [{ isSignal: true, alias: "scope", required: false }] }], bare: [{ type: i0.Input, args: [{ isSignal: true, alias: "bare", required: false }] }], providers: [{ type: i0.Input, args: [{ isSignal: true, alias: "providers", required: false }] }] } });
|
|
1160
|
+
}], ctorParameters: () => [], propDecorators: { node: [{ type: i0.Input, args: [{ isSignal: true, alias: "node", required: true }] }], engine: [{ type: i0.Input, args: [{ isSignal: true, alias: "engine", required: true }] }], tick: [{ type: i0.Input, args: [{ isSignal: true, alias: "tick", required: true }] }], scope: [{ type: i0.Input, args: [{ isSignal: true, alias: "scope", required: false }] }], bare: [{ type: i0.Input, args: [{ isSignal: true, alias: "bare", required: false }] }], providers: [{ type: i0.Input, args: [{ isSignal: true, alias: "providers", required: false }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], notify: [{ type: i0.Input, args: [{ isSignal: true, alias: "notify", required: false }] }] } });
|
|
939
1161
|
|
|
940
1162
|
/**
|
|
941
1163
|
* `<mform-renderer>` — renders a form at runtime from a `FormSchema` (or any
|
|
@@ -958,6 +1180,20 @@ class FormRendererComponent {
|
|
|
958
1180
|
optionsProviders = input({}, ...(ngDevMode ? [{ debugName: "optionsProviders" }] : /* istanbul ignore next */ []));
|
|
959
1181
|
/** Host-supplied message resolver (localization of validation messages). */
|
|
960
1182
|
messages = input(null, ...(ngDevMode ? [{ debugName: "messages" }] : /* istanbul ignore next */ []));
|
|
1183
|
+
/** Named custom validators (sync or async — Promises get pending states). */
|
|
1184
|
+
validators = input(null, ...(ngDevMode ? [{ debugName: "validators" }] : /* istanbul ignore next */ []));
|
|
1185
|
+
/** `edit` (default), `readonly` (no actions) or `disabled`. */
|
|
1186
|
+
mode = input('edit', ...(ngDevMode ? [{ debugName: "mode" }] : /* istanbul ignore next */ []));
|
|
1187
|
+
/**
|
|
1188
|
+
* Optional host submission: called after a VALID submit with the result;
|
|
1189
|
+
* rejections surface as the submission error state. Persistence transport
|
|
1190
|
+
* stays entirely on the host side.
|
|
1191
|
+
*/
|
|
1192
|
+
submitHandler = input(null, ...(ngDevMode ? [{ debugName: "submitHandler" }] : /* istanbul ignore next */ []));
|
|
1193
|
+
/** True while async validation or the submit handler is running. */
|
|
1194
|
+
submitting = signal(false, ...(ngDevMode ? [{ debugName: "submitting" }] : /* istanbul ignore next */ []));
|
|
1195
|
+
/** Message when the host submission rejected. */
|
|
1196
|
+
submitError = signal(null, ...(ngDevMode ? [{ debugName: "submitError" }] : /* istanbul ignore next */ []));
|
|
961
1197
|
/** Fixed renderer texts (override to localize). `{{count}}` in `summary`. */
|
|
962
1198
|
labels = input({
|
|
963
1199
|
previous: 'Previous',
|
|
@@ -980,6 +1216,8 @@ class FormRendererComponent {
|
|
|
980
1216
|
valueChanged = output();
|
|
981
1217
|
/** Bumped on every engine event; nodes read it to refresh. */
|
|
982
1218
|
tick = signal(0, ...(ngDevMode ? [{ debugName: "tick" }] : /* istanbul ignore next */ []));
|
|
1219
|
+
/** Handed to nodes so async completions re-render (zoneless-safe). */
|
|
1220
|
+
bumpTick = () => this.tick.update((v) => v + 1);
|
|
983
1221
|
/** Active wizard step index. */
|
|
984
1222
|
currentStep = signal(0, ...(ngDevMode ? [{ debugName: "currentStep" }] : /* istanbul ignore next */ []));
|
|
985
1223
|
resolved = computed(() => {
|
|
@@ -1010,6 +1248,7 @@ class FormRendererComponent {
|
|
|
1010
1248
|
? createFormEngine(schema, {
|
|
1011
1249
|
initialData: this.initialData() ?? undefined,
|
|
1012
1250
|
messages: this.messages() ?? undefined,
|
|
1251
|
+
validators: this.validators() ?? undefined,
|
|
1013
1252
|
})
|
|
1014
1253
|
: null;
|
|
1015
1254
|
}, ...(ngDevMode ? [{ debugName: "engine" }] : /* istanbul ignore next */ []));
|
|
@@ -1060,22 +1299,37 @@ class FormRendererComponent {
|
|
|
1060
1299
|
this.currentStep.update((index) => Math.min(this.steps().length - 1, index + 1));
|
|
1061
1300
|
}
|
|
1062
1301
|
// -- submission -----------------------------------------------------------
|
|
1063
|
-
onSubmit(event) {
|
|
1302
|
+
async onSubmit(event) {
|
|
1064
1303
|
event.preventDefault();
|
|
1065
1304
|
const engine = this.engine();
|
|
1066
|
-
if (!engine)
|
|
1305
|
+
if (!engine || this.submitting() || this.mode() !== 'edit')
|
|
1067
1306
|
return;
|
|
1068
|
-
|
|
1069
|
-
this.
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1307
|
+
this.submitting.set(true);
|
|
1308
|
+
this.submitError.set(null);
|
|
1309
|
+
try {
|
|
1310
|
+
const result = await engine.submitAsync();
|
|
1311
|
+
this.errorSummary.set(result.errors);
|
|
1312
|
+
if (!result.ok) {
|
|
1313
|
+
if (this.isWizard()) {
|
|
1314
|
+
const stepIndex = this.stepIndexForPath(result.errors[0]?.path ?? '');
|
|
1315
|
+
if (stepIndex >= 0)
|
|
1316
|
+
this.currentStep.set(stepIndex);
|
|
1317
|
+
}
|
|
1318
|
+
this.focusFirstError(result.errors);
|
|
1075
1319
|
}
|
|
1076
|
-
this.
|
|
1320
|
+
else if (this.submitHandler()) {
|
|
1321
|
+
try {
|
|
1322
|
+
await this.submitHandler()(result);
|
|
1323
|
+
}
|
|
1324
|
+
catch (error) {
|
|
1325
|
+
this.submitError.set(String(error instanceof Error ? error.message : error));
|
|
1326
|
+
}
|
|
1327
|
+
}
|
|
1328
|
+
this.submitted.emit(result);
|
|
1329
|
+
}
|
|
1330
|
+
finally {
|
|
1331
|
+
this.submitting.set(false);
|
|
1077
1332
|
}
|
|
1078
|
-
this.submitted.emit(result);
|
|
1079
1333
|
}
|
|
1080
1334
|
/** Finds the wizard step that owns a (possibly row-indexed) data path. */
|
|
1081
1335
|
stepIndexForPath(path) {
|
|
@@ -1095,7 +1349,7 @@ class FormRendererComponent {
|
|
|
1095
1349
|
queueMicrotask(() => this.documentRef.getElementById(id)?.focus());
|
|
1096
1350
|
}
|
|
1097
1351
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: FormRendererComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1098
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.19", type: FormRendererComponent, isStandalone: true, selector: "mform-renderer", inputs: { schema: { classPropertyName: "schema", publicName: "schema", isSignal: true, isRequired: false, transformFunction: null }, source: { classPropertyName: "source", publicName: "source", isSignal: true, isRequired: false, transformFunction: null }, initialData: { classPropertyName: "initialData", publicName: "initialData", isSignal: true, isRequired: false, transformFunction: null }, optionsProviders: { classPropertyName: "optionsProviders", publicName: "optionsProviders", isSignal: true, isRequired: false, transformFunction: null }, messages: { classPropertyName: "messages", publicName: "messages", isSignal: true, isRequired: false, transformFunction: null }, labels: { classPropertyName: "labels", publicName: "labels", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { submitted: "submitted", valueChanged: "valueChanged" }, ngImport: i0, template: `
|
|
1352
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.19", type: FormRendererComponent, isStandalone: true, selector: "mform-renderer", inputs: { schema: { classPropertyName: "schema", publicName: "schema", isSignal: true, isRequired: false, transformFunction: null }, source: { classPropertyName: "source", publicName: "source", isSignal: true, isRequired: false, transformFunction: null }, initialData: { classPropertyName: "initialData", publicName: "initialData", isSignal: true, isRequired: false, transformFunction: null }, optionsProviders: { classPropertyName: "optionsProviders", publicName: "optionsProviders", isSignal: true, isRequired: false, transformFunction: null }, messages: { classPropertyName: "messages", publicName: "messages", isSignal: true, isRequired: false, transformFunction: null }, validators: { classPropertyName: "validators", publicName: "validators", isSignal: true, isRequired: false, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, submitHandler: { classPropertyName: "submitHandler", publicName: "submitHandler", isSignal: true, isRequired: false, transformFunction: null }, labels: { classPropertyName: "labels", publicName: "labels", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { submitted: "submitted", valueChanged: "valueChanged" }, ngImport: i0, template: `
|
|
1099
1353
|
@if (engine(); as engine) {
|
|
1100
1354
|
<form class="mform-root" novalidate (submit)="onSubmit($event)">
|
|
1101
1355
|
@if (errorSummary().length > 0) {
|
|
@@ -1128,6 +1382,7 @@ class FormRendererComponent {
|
|
|
1128
1382
|
[engine]="engine"
|
|
1129
1383
|
[tick]="tick()"
|
|
1130
1384
|
[providers]="optionsProviders()"
|
|
1385
|
+
[mode]="mode()" [notify]="bumpTick"
|
|
1131
1386
|
[bare]="true"
|
|
1132
1387
|
/>
|
|
1133
1388
|
}
|
|
@@ -1141,18 +1396,23 @@ class FormRendererComponent {
|
|
|
1141
1396
|
<button type="button" class="mform-btn" (click)="nextStep()">
|
|
1142
1397
|
{{ labels().next }}
|
|
1143
1398
|
</button>
|
|
1144
|
-
} @else {
|
|
1145
|
-
<button type="submit" class="mform-btn"
|
|
1399
|
+
} @else if (mode() === 'edit') {
|
|
1400
|
+
<button type="submit" class="mform-btn" [disabled]="submitting()">
|
|
1401
|
+
{{ submitting() ? '…' : labels().submit }}
|
|
1402
|
+
</button>
|
|
1146
1403
|
}
|
|
1147
1404
|
</div>
|
|
1148
1405
|
} @else {
|
|
1149
1406
|
@for (node of engine.schema.fields; track node.key) {
|
|
1150
|
-
<mform-node [node]="node" [engine]="engine" [tick]="tick()" [providers]="optionsProviders()" />
|
|
1407
|
+
<mform-node [node]="node" [engine]="engine" [tick]="tick()" [providers]="optionsProviders()" [mode]="mode()" [notify]="bumpTick" />
|
|
1151
1408
|
}
|
|
1152
1409
|
}
|
|
1410
|
+
@if (submitError(); as error) {
|
|
1411
|
+
<div class="mform-summary" role="alert">{{ error }}</div>
|
|
1412
|
+
}
|
|
1153
1413
|
</form>
|
|
1154
1414
|
}
|
|
1155
|
-
`, isInline: true, styles: [".mform-root{display:flex;flex-direction:column;gap:var(--mform-gap, 16px);font-family:var(--mform-font, system-ui, \"Segoe UI\", sans-serif);color:var(--mform-text, #1f1b2e)}.mform-node{display:contents}.mform-field{display:flex;flex-direction:column;gap:6px}.mform-label{font-size:var(--mform-label-size, 13px);font-weight:var(--mform-label-weight, 600)}.mform-req{color:var(--mform-danger, #c62842);margin-left:2px}.mform-control{font:inherit;width:100%;padding:var(--mform-control-padding, 9px 12px);border:1px solid var(--mform-border, #d9d5e6);border-radius:var(--mform-control-radius, 8px);background:var(--mform-control-bg, #fff);color:inherit;box-sizing:border-box}textarea.mform-control{min-height:72px;resize:vertical}.mform-control:focus-visible{outline:2px solid var(--mform-focus, #8e6ff0);outline-offset:1px;border-color:var(--mform-focus, #8e6ff0)}.mform-field.has-error .mform-control{border-color:var(--mform-danger, #c62842)}.mform-desc{font-size:12px;color:var(--mform-muted, #6b6580)}.mform-error{font-size:12px;color:var(--mform-danger, #c62842)}.mform-choices{display:flex;flex-direction:column;gap:6px}.mform-check{display:flex;align-items:center;gap:8px;font-size:14px;cursor:pointer}.mform-check input{width:16px;height:16px;accent-color:var(--mform-primary, #6d4bd0)}.mform-panel{border:1px solid var(--mform-border, #d9d5e6);border-radius:var(--mform-radius, 10px);overflow:hidden}.mform-panel-head{background:var(--mform-panel-head-bg, linear-gradient(90deg, #efeaff, #f8f6ff));color:var(--mform-panel-head-text, inherit);padding:10px 16px;font-weight:600;font-size:14px;border-bottom:1px solid var(--mform-border, #d9d5e6)}.mform-panel-body,.mform-group,.mform-column,.mform-tabpanel{display:flex;flex-direction:column;gap:var(--mform-gap, 16px)}.mform-panel-body{padding:var(--mform-panel-padding, 16px)}.mform-columns{display:grid;gap:var(--mform-gap, 16px)}@media(max-width:640px){.mform-columns{grid-template-columns:1fr!important}}.mform-affix{display:flex;align-items:center}.mform-affix.has-affix{border:1px solid var(--mform-border, #d9d5e6);border-radius:var(--mform-control-radius, 8px);background:var(--mform-control-bg, #fff)}.mform-affix.has-affix .mform-control{border:none;background:none;flex:1}.mform-affix.has-affix:focus-within{outline:2px solid var(--mform-focus, #8e6ff0);outline-offset:1px}.mform-prefix,.mform-suffix{padding:0 10px;color:var(--mform-muted, #6b6580);font-size:13px;white-space:nowrap}.mform-tags{display:flex;flex-wrap:wrap;gap:6px;align-items:center}.mform-tag{background:var(--mform-primary, #6d4bd0);color:var(--mform-primary-contrast, #fff);border-radius:999px;padding:3px 10px;font-size:12px;display:inline-flex;align-items:center;gap:6px}.mform-tag button{border:none;background:none;color:inherit;cursor:pointer;font-size:12px;padding:0;line-height:1}.mform-tag-input{flex:1;min-width:140px;width:auto}.mform-tabbar{display:flex;gap:4px;border-bottom:2px solid var(--mform-border, #d9d5e6);margin-bottom:14px}.mform-tab{padding:8px 18px;font:inherit;font-size:14px;border:none;background:none;color:var(--mform-muted, #6b6580);cursor:pointer}.mform-tab.is-active{color:var(--mform-primary, #6d4bd0);font-weight:600;border-bottom:2px solid var(--mform-primary, #6d4bd0);margin-bottom:-2px}.mform-grid-row{display:flex;align-items:flex-start;gap:8px;padding:10px 0;border-bottom:1px dashed var(--mform-border, #d9d5e6)}.mform-grid-fields{flex:1;display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:var(--mform-gap, 16px)}.mform-row-remove{border:none;background:none;color:var(--mform-muted, #6b6580);font:inherit;cursor:pointer;padding:6px 8px}.mform-row-remove:hover{color:var(--mform-danger, #c62842)}.mform-row-add{align-self:flex-start;background:none;border:1px dashed var(--mform-primary, #6d4bd0);color:var(--mform-primary, #6d4bd0);padding:7px 14px;border-radius:var(--mform-control-radius, 8px);font:inherit;font-size:13px;cursor:pointer}.mform-btn{align-self:flex-start;background:var(--mform-primary, #6d4bd0);color:var(--mform-primary-contrast, #fff);border:none;border-radius:var(--mform-control-radius, 8px);padding:11px 26px;font:inherit;font-weight:600;cursor:pointer}.mform-btn:hover{filter:brightness(1.08)}.mform-btn:focus-visible{outline:2px solid var(--mform-focus, #8e6ff0);outline-offset:2px}.mform-content{font-size:14px}.mform-unsupported{font-size:13px;color:var(--mform-muted, #6b6580);border:1px dashed var(--mform-border, #d9d5e6);border-radius:var(--mform-radius, 10px);padding:10px 14px}.mform-steps{display:flex;gap:18px;list-style:none;margin:0 0 4px;padding:0 0 12px;border-bottom:1px solid var(--mform-border, #d9d5e6);flex-wrap:wrap}.mform-step{display:flex;align-items:center;gap:8px;font-size:13px;color:var(--mform-muted, #6b6580)}.mform-step-index{display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;border-radius:50%;border:1.5px solid var(--mform-border, #d9d5e6);font-size:11px;font-weight:600}.mform-step.is-active{color:var(--mform-primary, #6d4bd0);font-weight:600}.mform-step.is-active .mform-step-index{border-color:var(--mform-primary, #6d4bd0);background:var(--mform-primary, #6d4bd0);color:var(--mform-primary-contrast, #fff)}.mform-step.is-done .mform-step-index{border-color:var(--mform-primary, #6d4bd0);color:var(--mform-primary, #6d4bd0)}.mform-wizard-nav{display:flex;gap:10px;padding-top:4px}.mform-btn-secondary{background:none;border:1px solid var(--mform-primary, #6d4bd0);color:var(--mform-primary, #6d4bd0)}.mform-summary{border:1px solid var(--mform-danger, #c62842);background:var(--mform-danger-bg, #fdf0f2);color:var(--mform-danger, #c62842);border-radius:var(--mform-radius, 10px);padding:12px 16px;font-size:13px}.mform-summary ul{margin:6px 0 0;padding-left:18px}\n"], dependencies: [{ kind: "component", type: FormNodeComponent, selector: "mform-node", inputs: ["node", "engine", "tick", "scope", "bare", "providers"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
1415
|
+
`, isInline: true, styles: [".mform-root{display:flex;flex-direction:column;gap:var(--mform-gap, 16px);font-family:var(--mform-font, system-ui, \"Segoe UI\", sans-serif);color:var(--mform-text, #1f1b2e)}.mform-node{display:contents}.mform-field{display:flex;flex-direction:column;gap:6px}.mform-label{font-size:var(--mform-label-size, 13px);font-weight:var(--mform-label-weight, 600)}.mform-req{color:var(--mform-danger, #c62842);margin-left:2px}.mform-control{font:inherit;width:100%;padding:var(--mform-control-padding, 9px 12px);border:1px solid var(--mform-border, #d9d5e6);border-radius:var(--mform-control-radius, 8px);background:var(--mform-control-bg, #fff);color:inherit;box-sizing:border-box}textarea.mform-control{min-height:72px;resize:vertical}.mform-control:focus-visible{outline:2px solid var(--mform-focus, #8e6ff0);outline-offset:1px;border-color:var(--mform-focus, #8e6ff0)}.mform-field.has-error .mform-control{border-color:var(--mform-danger, #c62842)}.mform-desc{font-size:12px;color:var(--mform-muted, #6b6580)}.mform-error{font-size:12px;color:var(--mform-danger, #c62842)}.mform-choices{display:flex;flex-direction:column;gap:6px}.mform-check{display:flex;align-items:center;gap:8px;font-size:14px;cursor:pointer}.mform-check input{width:16px;height:16px;accent-color:var(--mform-primary, #6d4bd0)}.mform-panel{border:1px solid var(--mform-border, #d9d5e6);border-radius:var(--mform-radius, 10px);overflow:hidden}.mform-panel-head{background:var(--mform-panel-head-bg, linear-gradient(90deg, #efeaff, #f8f6ff));color:var(--mform-panel-head-text, inherit);padding:10px 16px;font-weight:600;font-size:14px;border-bottom:1px solid var(--mform-border, #d9d5e6)}.mform-panel-body,.mform-group,.mform-column,.mform-tabpanel{display:flex;flex-direction:column;gap:var(--mform-gap, 16px)}.mform-panel-body{padding:var(--mform-panel-padding, 16px)}.mform-columns{display:grid;gap:var(--mform-gap, 16px)}@media(max-width:640px){.mform-columns{grid-template-columns:1fr!important}}.mform-affix{display:flex;align-items:center}.mform-affix.has-affix{border:1px solid var(--mform-border, #d9d5e6);border-radius:var(--mform-control-radius, 8px);background:var(--mform-control-bg, #fff)}.mform-affix.has-affix .mform-control{border:none;background:none;flex:1}.mform-affix.has-affix:focus-within{outline:2px solid var(--mform-focus, #8e6ff0);outline-offset:1px}.mform-prefix,.mform-suffix{padding:0 10px;color:var(--mform-muted, #6b6580);font-size:13px;white-space:nowrap}.mform-tags{display:flex;flex-wrap:wrap;gap:6px;align-items:center}.mform-tag{background:var(--mform-primary, #6d4bd0);color:var(--mform-primary-contrast, #fff);border-radius:999px;padding:3px 10px;font-size:12px;display:inline-flex;align-items:center;gap:6px}.mform-tag button{border:none;background:none;color:inherit;cursor:pointer;font-size:12px;padding:0;line-height:1}.mform-tag-input{flex:1;min-width:140px;width:auto}.mform-field.is-pending .mform-control{background-image:linear-gradient(90deg,transparent 0%,rgb(109 75 208 / 12%) 50%,transparent 100%);background-size:200% 100%;animation:mform-pending 1.2s linear infinite}@keyframes mform-pending{0%{background-position:200% 0}to{background-position:-200% 0}}.mform-file-name{font-size:12px;color:var(--mform-muted, #6b6580);padding:2px 0}.mform-reference{position:relative}.mform-ref-list{position:absolute;top:calc(100% + 4px);left:0;right:0;z-index:10;margin:0;padding:4px;list-style:none;background:var(--mform-control-bg, #fff);border:1px solid var(--mform-border, #d9d5e6);border-radius:var(--mform-control-radius, 8px);box-shadow:0 8px 24px #1f1b2e1f;max-height:220px;overflow-y:auto}.mform-ref-list button{display:block;width:100%;text-align:left;font:inherit;font-size:13px;padding:7px 10px;border:none;border-radius:6px;background:none;color:inherit;cursor:pointer}.mform-ref-list button:hover{background:var(--mform-bg, #f6f5fa)}.mform-tabbar{display:flex;gap:4px;border-bottom:2px solid var(--mform-border, #d9d5e6);margin-bottom:14px}.mform-tab{padding:8px 18px;font:inherit;font-size:14px;border:none;background:none;color:var(--mform-muted, #6b6580);cursor:pointer}.mform-tab.is-active{color:var(--mform-primary, #6d4bd0);font-weight:600;border-bottom:2px solid var(--mform-primary, #6d4bd0);margin-bottom:-2px}.mform-grid-row{display:flex;align-items:flex-start;gap:8px;padding:10px 0;border-bottom:1px dashed var(--mform-border, #d9d5e6)}.mform-grid-fields{flex:1;display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:var(--mform-gap, 16px)}.mform-row-remove{border:none;background:none;color:var(--mform-muted, #6b6580);font:inherit;cursor:pointer;padding:6px 8px}.mform-row-remove:hover{color:var(--mform-danger, #c62842)}.mform-row-add{align-self:flex-start;background:none;border:1px dashed var(--mform-primary, #6d4bd0);color:var(--mform-primary, #6d4bd0);padding:7px 14px;border-radius:var(--mform-control-radius, 8px);font:inherit;font-size:13px;cursor:pointer}.mform-btn{align-self:flex-start;background:var(--mform-primary, #6d4bd0);color:var(--mform-primary-contrast, #fff);border:none;border-radius:var(--mform-control-radius, 8px);padding:11px 26px;font:inherit;font-weight:600;cursor:pointer}.mform-btn:hover{filter:brightness(1.08)}.mform-btn:focus-visible{outline:2px solid var(--mform-focus, #8e6ff0);outline-offset:2px}.mform-content{font-size:14px}.mform-unsupported{font-size:13px;color:var(--mform-muted, #6b6580);border:1px dashed var(--mform-border, #d9d5e6);border-radius:var(--mform-radius, 10px);padding:10px 14px}.mform-steps{display:flex;gap:18px;list-style:none;margin:0 0 4px;padding:0 0 12px;border-bottom:1px solid var(--mform-border, #d9d5e6);flex-wrap:wrap}.mform-step{display:flex;align-items:center;gap:8px;font-size:13px;color:var(--mform-muted, #6b6580)}.mform-step-index{display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;border-radius:50%;border:1.5px solid var(--mform-border, #d9d5e6);font-size:11px;font-weight:600}.mform-step.is-active{color:var(--mform-primary, #6d4bd0);font-weight:600}.mform-step.is-active .mform-step-index{border-color:var(--mform-primary, #6d4bd0);background:var(--mform-primary, #6d4bd0);color:var(--mform-primary-contrast, #fff)}.mform-step.is-done .mform-step-index{border-color:var(--mform-primary, #6d4bd0);color:var(--mform-primary, #6d4bd0)}.mform-wizard-nav{display:flex;gap:10px;padding-top:4px}.mform-btn-secondary{background:none;border:1px solid var(--mform-primary, #6d4bd0);color:var(--mform-primary, #6d4bd0)}.mform-summary{border:1px solid var(--mform-danger, #c62842);background:var(--mform-danger-bg, #fdf0f2);color:var(--mform-danger, #c62842);border-radius:var(--mform-radius, 10px);padding:12px 16px;font-size:13px}.mform-summary ul{margin:6px 0 0;padding-left:18px}\n"], dependencies: [{ kind: "component", type: FormNodeComponent, selector: "mform-node", inputs: ["node", "engine", "tick", "scope", "bare", "providers", "mode", "notify"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
|
|
1156
1416
|
}
|
|
1157
1417
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: FormRendererComponent, decorators: [{
|
|
1158
1418
|
type: Component,
|
|
@@ -1189,6 +1449,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImpo
|
|
|
1189
1449
|
[engine]="engine"
|
|
1190
1450
|
[tick]="tick()"
|
|
1191
1451
|
[providers]="optionsProviders()"
|
|
1452
|
+
[mode]="mode()" [notify]="bumpTick"
|
|
1192
1453
|
[bare]="true"
|
|
1193
1454
|
/>
|
|
1194
1455
|
}
|
|
@@ -1202,19 +1463,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImpo
|
|
|
1202
1463
|
<button type="button" class="mform-btn" (click)="nextStep()">
|
|
1203
1464
|
{{ labels().next }}
|
|
1204
1465
|
</button>
|
|
1205
|
-
} @else {
|
|
1206
|
-
<button type="submit" class="mform-btn"
|
|
1466
|
+
} @else if (mode() === 'edit') {
|
|
1467
|
+
<button type="submit" class="mform-btn" [disabled]="submitting()">
|
|
1468
|
+
{{ submitting() ? '…' : labels().submit }}
|
|
1469
|
+
</button>
|
|
1207
1470
|
}
|
|
1208
1471
|
</div>
|
|
1209
1472
|
} @else {
|
|
1210
1473
|
@for (node of engine.schema.fields; track node.key) {
|
|
1211
|
-
<mform-node [node]="node" [engine]="engine" [tick]="tick()" [providers]="optionsProviders()" />
|
|
1474
|
+
<mform-node [node]="node" [engine]="engine" [tick]="tick()" [providers]="optionsProviders()" [mode]="mode()" [notify]="bumpTick" />
|
|
1212
1475
|
}
|
|
1213
1476
|
}
|
|
1477
|
+
@if (submitError(); as error) {
|
|
1478
|
+
<div class="mform-summary" role="alert">{{ error }}</div>
|
|
1479
|
+
}
|
|
1214
1480
|
</form>
|
|
1215
1481
|
}
|
|
1216
|
-
`, styles: [".mform-root{display:flex;flex-direction:column;gap:var(--mform-gap, 16px);font-family:var(--mform-font, system-ui, \"Segoe UI\", sans-serif);color:var(--mform-text, #1f1b2e)}.mform-node{display:contents}.mform-field{display:flex;flex-direction:column;gap:6px}.mform-label{font-size:var(--mform-label-size, 13px);font-weight:var(--mform-label-weight, 600)}.mform-req{color:var(--mform-danger, #c62842);margin-left:2px}.mform-control{font:inherit;width:100%;padding:var(--mform-control-padding, 9px 12px);border:1px solid var(--mform-border, #d9d5e6);border-radius:var(--mform-control-radius, 8px);background:var(--mform-control-bg, #fff);color:inherit;box-sizing:border-box}textarea.mform-control{min-height:72px;resize:vertical}.mform-control:focus-visible{outline:2px solid var(--mform-focus, #8e6ff0);outline-offset:1px;border-color:var(--mform-focus, #8e6ff0)}.mform-field.has-error .mform-control{border-color:var(--mform-danger, #c62842)}.mform-desc{font-size:12px;color:var(--mform-muted, #6b6580)}.mform-error{font-size:12px;color:var(--mform-danger, #c62842)}.mform-choices{display:flex;flex-direction:column;gap:6px}.mform-check{display:flex;align-items:center;gap:8px;font-size:14px;cursor:pointer}.mform-check input{width:16px;height:16px;accent-color:var(--mform-primary, #6d4bd0)}.mform-panel{border:1px solid var(--mform-border, #d9d5e6);border-radius:var(--mform-radius, 10px);overflow:hidden}.mform-panel-head{background:var(--mform-panel-head-bg, linear-gradient(90deg, #efeaff, #f8f6ff));color:var(--mform-panel-head-text, inherit);padding:10px 16px;font-weight:600;font-size:14px;border-bottom:1px solid var(--mform-border, #d9d5e6)}.mform-panel-body,.mform-group,.mform-column,.mform-tabpanel{display:flex;flex-direction:column;gap:var(--mform-gap, 16px)}.mform-panel-body{padding:var(--mform-panel-padding, 16px)}.mform-columns{display:grid;gap:var(--mform-gap, 16px)}@media(max-width:640px){.mform-columns{grid-template-columns:1fr!important}}.mform-affix{display:flex;align-items:center}.mform-affix.has-affix{border:1px solid var(--mform-border, #d9d5e6);border-radius:var(--mform-control-radius, 8px);background:var(--mform-control-bg, #fff)}.mform-affix.has-affix .mform-control{border:none;background:none;flex:1}.mform-affix.has-affix:focus-within{outline:2px solid var(--mform-focus, #8e6ff0);outline-offset:1px}.mform-prefix,.mform-suffix{padding:0 10px;color:var(--mform-muted, #6b6580);font-size:13px;white-space:nowrap}.mform-tags{display:flex;flex-wrap:wrap;gap:6px;align-items:center}.mform-tag{background:var(--mform-primary, #6d4bd0);color:var(--mform-primary-contrast, #fff);border-radius:999px;padding:3px 10px;font-size:12px;display:inline-flex;align-items:center;gap:6px}.mform-tag button{border:none;background:none;color:inherit;cursor:pointer;font-size:12px;padding:0;line-height:1}.mform-tag-input{flex:1;min-width:140px;width:auto}.mform-tabbar{display:flex;gap:4px;border-bottom:2px solid var(--mform-border, #d9d5e6);margin-bottom:14px}.mform-tab{padding:8px 18px;font:inherit;font-size:14px;border:none;background:none;color:var(--mform-muted, #6b6580);cursor:pointer}.mform-tab.is-active{color:var(--mform-primary, #6d4bd0);font-weight:600;border-bottom:2px solid var(--mform-primary, #6d4bd0);margin-bottom:-2px}.mform-grid-row{display:flex;align-items:flex-start;gap:8px;padding:10px 0;border-bottom:1px dashed var(--mform-border, #d9d5e6)}.mform-grid-fields{flex:1;display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:var(--mform-gap, 16px)}.mform-row-remove{border:none;background:none;color:var(--mform-muted, #6b6580);font:inherit;cursor:pointer;padding:6px 8px}.mform-row-remove:hover{color:var(--mform-danger, #c62842)}.mform-row-add{align-self:flex-start;background:none;border:1px dashed var(--mform-primary, #6d4bd0);color:var(--mform-primary, #6d4bd0);padding:7px 14px;border-radius:var(--mform-control-radius, 8px);font:inherit;font-size:13px;cursor:pointer}.mform-btn{align-self:flex-start;background:var(--mform-primary, #6d4bd0);color:var(--mform-primary-contrast, #fff);border:none;border-radius:var(--mform-control-radius, 8px);padding:11px 26px;font:inherit;font-weight:600;cursor:pointer}.mform-btn:hover{filter:brightness(1.08)}.mform-btn:focus-visible{outline:2px solid var(--mform-focus, #8e6ff0);outline-offset:2px}.mform-content{font-size:14px}.mform-unsupported{font-size:13px;color:var(--mform-muted, #6b6580);border:1px dashed var(--mform-border, #d9d5e6);border-radius:var(--mform-radius, 10px);padding:10px 14px}.mform-steps{display:flex;gap:18px;list-style:none;margin:0 0 4px;padding:0 0 12px;border-bottom:1px solid var(--mform-border, #d9d5e6);flex-wrap:wrap}.mform-step{display:flex;align-items:center;gap:8px;font-size:13px;color:var(--mform-muted, #6b6580)}.mform-step-index{display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;border-radius:50%;border:1.5px solid var(--mform-border, #d9d5e6);font-size:11px;font-weight:600}.mform-step.is-active{color:var(--mform-primary, #6d4bd0);font-weight:600}.mform-step.is-active .mform-step-index{border-color:var(--mform-primary, #6d4bd0);background:var(--mform-primary, #6d4bd0);color:var(--mform-primary-contrast, #fff)}.mform-step.is-done .mform-step-index{border-color:var(--mform-primary, #6d4bd0);color:var(--mform-primary, #6d4bd0)}.mform-wizard-nav{display:flex;gap:10px;padding-top:4px}.mform-btn-secondary{background:none;border:1px solid var(--mform-primary, #6d4bd0);color:var(--mform-primary, #6d4bd0)}.mform-summary{border:1px solid var(--mform-danger, #c62842);background:var(--mform-danger-bg, #fdf0f2);color:var(--mform-danger, #c62842);border-radius:var(--mform-radius, 10px);padding:12px 16px;font-size:13px}.mform-summary ul{margin:6px 0 0;padding-left:18px}\n"] }]
|
|
1217
|
-
}], ctorParameters: () => [], propDecorators: { schema: [{ type: i0.Input, args: [{ isSignal: true, alias: "schema", required: false }] }], source: [{ type: i0.Input, args: [{ isSignal: true, alias: "source", required: false }] }], initialData: [{ type: i0.Input, args: [{ isSignal: true, alias: "initialData", required: false }] }], optionsProviders: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionsProviders", required: false }] }], messages: [{ type: i0.Input, args: [{ isSignal: true, alias: "messages", required: false }] }], labels: [{ type: i0.Input, args: [{ isSignal: true, alias: "labels", required: false }] }], submitted: [{ type: i0.Output, args: ["submitted"] }], valueChanged: [{ type: i0.Output, args: ["valueChanged"] }] } });
|
|
1482
|
+
`, styles: [".mform-root{display:flex;flex-direction:column;gap:var(--mform-gap, 16px);font-family:var(--mform-font, system-ui, \"Segoe UI\", sans-serif);color:var(--mform-text, #1f1b2e)}.mform-node{display:contents}.mform-field{display:flex;flex-direction:column;gap:6px}.mform-label{font-size:var(--mform-label-size, 13px);font-weight:var(--mform-label-weight, 600)}.mform-req{color:var(--mform-danger, #c62842);margin-left:2px}.mform-control{font:inherit;width:100%;padding:var(--mform-control-padding, 9px 12px);border:1px solid var(--mform-border, #d9d5e6);border-radius:var(--mform-control-radius, 8px);background:var(--mform-control-bg, #fff);color:inherit;box-sizing:border-box}textarea.mform-control{min-height:72px;resize:vertical}.mform-control:focus-visible{outline:2px solid var(--mform-focus, #8e6ff0);outline-offset:1px;border-color:var(--mform-focus, #8e6ff0)}.mform-field.has-error .mform-control{border-color:var(--mform-danger, #c62842)}.mform-desc{font-size:12px;color:var(--mform-muted, #6b6580)}.mform-error{font-size:12px;color:var(--mform-danger, #c62842)}.mform-choices{display:flex;flex-direction:column;gap:6px}.mform-check{display:flex;align-items:center;gap:8px;font-size:14px;cursor:pointer}.mform-check input{width:16px;height:16px;accent-color:var(--mform-primary, #6d4bd0)}.mform-panel{border:1px solid var(--mform-border, #d9d5e6);border-radius:var(--mform-radius, 10px);overflow:hidden}.mform-panel-head{background:var(--mform-panel-head-bg, linear-gradient(90deg, #efeaff, #f8f6ff));color:var(--mform-panel-head-text, inherit);padding:10px 16px;font-weight:600;font-size:14px;border-bottom:1px solid var(--mform-border, #d9d5e6)}.mform-panel-body,.mform-group,.mform-column,.mform-tabpanel{display:flex;flex-direction:column;gap:var(--mform-gap, 16px)}.mform-panel-body{padding:var(--mform-panel-padding, 16px)}.mform-columns{display:grid;gap:var(--mform-gap, 16px)}@media(max-width:640px){.mform-columns{grid-template-columns:1fr!important}}.mform-affix{display:flex;align-items:center}.mform-affix.has-affix{border:1px solid var(--mform-border, #d9d5e6);border-radius:var(--mform-control-radius, 8px);background:var(--mform-control-bg, #fff)}.mform-affix.has-affix .mform-control{border:none;background:none;flex:1}.mform-affix.has-affix:focus-within{outline:2px solid var(--mform-focus, #8e6ff0);outline-offset:1px}.mform-prefix,.mform-suffix{padding:0 10px;color:var(--mform-muted, #6b6580);font-size:13px;white-space:nowrap}.mform-tags{display:flex;flex-wrap:wrap;gap:6px;align-items:center}.mform-tag{background:var(--mform-primary, #6d4bd0);color:var(--mform-primary-contrast, #fff);border-radius:999px;padding:3px 10px;font-size:12px;display:inline-flex;align-items:center;gap:6px}.mform-tag button{border:none;background:none;color:inherit;cursor:pointer;font-size:12px;padding:0;line-height:1}.mform-tag-input{flex:1;min-width:140px;width:auto}.mform-field.is-pending .mform-control{background-image:linear-gradient(90deg,transparent 0%,rgb(109 75 208 / 12%) 50%,transparent 100%);background-size:200% 100%;animation:mform-pending 1.2s linear infinite}@keyframes mform-pending{0%{background-position:200% 0}to{background-position:-200% 0}}.mform-file-name{font-size:12px;color:var(--mform-muted, #6b6580);padding:2px 0}.mform-reference{position:relative}.mform-ref-list{position:absolute;top:calc(100% + 4px);left:0;right:0;z-index:10;margin:0;padding:4px;list-style:none;background:var(--mform-control-bg, #fff);border:1px solid var(--mform-border, #d9d5e6);border-radius:var(--mform-control-radius, 8px);box-shadow:0 8px 24px #1f1b2e1f;max-height:220px;overflow-y:auto}.mform-ref-list button{display:block;width:100%;text-align:left;font:inherit;font-size:13px;padding:7px 10px;border:none;border-radius:6px;background:none;color:inherit;cursor:pointer}.mform-ref-list button:hover{background:var(--mform-bg, #f6f5fa)}.mform-tabbar{display:flex;gap:4px;border-bottom:2px solid var(--mform-border, #d9d5e6);margin-bottom:14px}.mform-tab{padding:8px 18px;font:inherit;font-size:14px;border:none;background:none;color:var(--mform-muted, #6b6580);cursor:pointer}.mform-tab.is-active{color:var(--mform-primary, #6d4bd0);font-weight:600;border-bottom:2px solid var(--mform-primary, #6d4bd0);margin-bottom:-2px}.mform-grid-row{display:flex;align-items:flex-start;gap:8px;padding:10px 0;border-bottom:1px dashed var(--mform-border, #d9d5e6)}.mform-grid-fields{flex:1;display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:var(--mform-gap, 16px)}.mform-row-remove{border:none;background:none;color:var(--mform-muted, #6b6580);font:inherit;cursor:pointer;padding:6px 8px}.mform-row-remove:hover{color:var(--mform-danger, #c62842)}.mform-row-add{align-self:flex-start;background:none;border:1px dashed var(--mform-primary, #6d4bd0);color:var(--mform-primary, #6d4bd0);padding:7px 14px;border-radius:var(--mform-control-radius, 8px);font:inherit;font-size:13px;cursor:pointer}.mform-btn{align-self:flex-start;background:var(--mform-primary, #6d4bd0);color:var(--mform-primary-contrast, #fff);border:none;border-radius:var(--mform-control-radius, 8px);padding:11px 26px;font:inherit;font-weight:600;cursor:pointer}.mform-btn:hover{filter:brightness(1.08)}.mform-btn:focus-visible{outline:2px solid var(--mform-focus, #8e6ff0);outline-offset:2px}.mform-content{font-size:14px}.mform-unsupported{font-size:13px;color:var(--mform-muted, #6b6580);border:1px dashed var(--mform-border, #d9d5e6);border-radius:var(--mform-radius, 10px);padding:10px 14px}.mform-steps{display:flex;gap:18px;list-style:none;margin:0 0 4px;padding:0 0 12px;border-bottom:1px solid var(--mform-border, #d9d5e6);flex-wrap:wrap}.mform-step{display:flex;align-items:center;gap:8px;font-size:13px;color:var(--mform-muted, #6b6580)}.mform-step-index{display:inline-flex;align-items:center;justify-content:center;width:22px;height:22px;border-radius:50%;border:1.5px solid var(--mform-border, #d9d5e6);font-size:11px;font-weight:600}.mform-step.is-active{color:var(--mform-primary, #6d4bd0);font-weight:600}.mform-step.is-active .mform-step-index{border-color:var(--mform-primary, #6d4bd0);background:var(--mform-primary, #6d4bd0);color:var(--mform-primary-contrast, #fff)}.mform-step.is-done .mform-step-index{border-color:var(--mform-primary, #6d4bd0);color:var(--mform-primary, #6d4bd0)}.mform-wizard-nav{display:flex;gap:10px;padding-top:4px}.mform-btn-secondary{background:none;border:1px solid var(--mform-primary, #6d4bd0);color:var(--mform-primary, #6d4bd0)}.mform-summary{border:1px solid var(--mform-danger, #c62842);background:var(--mform-danger-bg, #fdf0f2);color:var(--mform-danger, #c62842);border-radius:var(--mform-radius, 10px);padding:12px 16px;font-size:13px}.mform-summary ul{margin:6px 0 0;padding-left:18px}\n"] }]
|
|
1483
|
+
}], ctorParameters: () => [], propDecorators: { schema: [{ type: i0.Input, args: [{ isSignal: true, alias: "schema", required: false }] }], source: [{ type: i0.Input, args: [{ isSignal: true, alias: "source", required: false }] }], initialData: [{ type: i0.Input, args: [{ isSignal: true, alias: "initialData", required: false }] }], optionsProviders: [{ type: i0.Input, args: [{ isSignal: true, alias: "optionsProviders", required: false }] }], messages: [{ type: i0.Input, args: [{ isSignal: true, alias: "messages", required: false }] }], validators: [{ type: i0.Input, args: [{ isSignal: true, alias: "validators", required: false }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], submitHandler: [{ type: i0.Input, args: [{ isSignal: true, alias: "submitHandler", required: false }] }], labels: [{ type: i0.Input, args: [{ isSignal: true, alias: "labels", required: false }] }], submitted: [{ type: i0.Output, args: ["submitted"] }], valueChanged: [{ type: i0.Output, args: ["valueChanged"] }] } });
|
|
1218
1484
|
|
|
1219
1485
|
/*
|
|
1220
1486
|
* Runtime entry point of @mosaicoo/form-angular.
|
|
@@ -1229,5 +1495,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImpo
|
|
|
1229
1495
|
* Generated bundle index. Do not edit.
|
|
1230
1496
|
*/
|
|
1231
1497
|
|
|
1232
|
-
export { FormNodeComponent, FormRendererComponent, MFORM_FIELD_COMPONENTS, MFORM_REMOTE_FETCHER, provideMosaicooForm };
|
|
1498
|
+
export { FormNodeComponent, FormRendererComponent, MFORM_FIELD_COMPONENTS, MFORM_REMOTE_FETCHER, MFORM_UPLOAD, provideMosaicooForm };
|
|
1233
1499
|
//# sourceMappingURL=mosaicoo-form-angular.mjs.map
|