@mosaicoo/form-angular 0.2.0 → 0.4.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 +18 -0
- package/fesm2022/mosaicoo-form-angular-designer.mjs +1305 -0
- package/fesm2022/mosaicoo-form-angular-designer.mjs.map +1 -0
- package/fesm2022/mosaicoo-form-angular.mjs +464 -112
- package/fesm2022/mosaicoo-form-angular.mjs.map +1 -1
- package/package.json +6 -2
- package/types/mosaicoo-form-angular-designer.d.ts +155 -0
- package/types/mosaicoo-form-angular.d.ts +137 -66
|
@@ -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, 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();
|
|
@@ -119,7 +148,11 @@ class FormNodeComponent {
|
|
|
119
148
|
if (!provider)
|
|
120
149
|
return; // no provider: static options/fallback stand
|
|
121
150
|
Promise.resolve(provider({ field: node, path, data: engine.getData(), args: source.args }))
|
|
122
|
-
.then(
|
|
151
|
+
.then((options) => {
|
|
152
|
+
apply(options);
|
|
153
|
+
this.changeDetector.markForCheck();
|
|
154
|
+
this.notify()();
|
|
155
|
+
})
|
|
123
156
|
.catch(() => { }); // provider failure keeps the static fallback
|
|
124
157
|
}
|
|
125
158
|
else {
|
|
@@ -133,6 +166,7 @@ class FormNodeComponent {
|
|
|
133
166
|
label: String(item[source.labelKey ?? 'label'] ?? item[source.valueKey ?? 'value'] ?? ''),
|
|
134
167
|
value: (item[source.valueKey ?? 'value'] ?? ''),
|
|
135
168
|
}))))
|
|
169
|
+
.then(() => this.changeDetector.markForCheck())
|
|
136
170
|
.catch(() => { });
|
|
137
171
|
}
|
|
138
172
|
});
|
|
@@ -217,7 +251,12 @@ class FormNodeComponent {
|
|
|
217
251
|
}
|
|
218
252
|
stringValue() {
|
|
219
253
|
const value = this.value();
|
|
220
|
-
|
|
254
|
+
if (value === undefined || value === null)
|
|
255
|
+
return '';
|
|
256
|
+
const mask = this.field().format?.mask;
|
|
257
|
+
if (mask && typeof value === 'string')
|
|
258
|
+
return maskValue(value, mask).masked;
|
|
259
|
+
return String(value);
|
|
221
260
|
}
|
|
222
261
|
mapValue() {
|
|
223
262
|
const value = this.value();
|
|
@@ -275,7 +314,15 @@ class FormNodeComponent {
|
|
|
275
314
|
this.engine().setValue(this.path(), value);
|
|
276
315
|
}
|
|
277
316
|
setString(event) {
|
|
278
|
-
const
|
|
317
|
+
const target = event.target;
|
|
318
|
+
const raw = target.value;
|
|
319
|
+
const mask = this.field().format?.mask;
|
|
320
|
+
if (mask) {
|
|
321
|
+
const { masked, raw: stripped } = maskValue(raw, mask);
|
|
322
|
+
target.value = masked; // the control always shows the masked shape
|
|
323
|
+
this.setValue(this.field().format?.keepMask ? masked : stripped);
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
279
326
|
if (this.field().dataType === 'number') {
|
|
280
327
|
const parsed = raw === '' ? undefined : Number(raw);
|
|
281
328
|
this.setValue(parsed !== undefined && Number.isFinite(parsed) ? parsed : undefined);
|
|
@@ -296,11 +343,124 @@ class FormNodeComponent {
|
|
|
296
343
|
}
|
|
297
344
|
setFiles(event) {
|
|
298
345
|
const files = event.target.files;
|
|
299
|
-
|
|
346
|
+
const list = files ? Array.from(files) : [];
|
|
347
|
+
if (!this.uploader || list.length === 0) {
|
|
348
|
+
// no host uploader: only file names are stored
|
|
349
|
+
this.setValue(list.map((f) => f.name));
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
352
|
+
const field = this.field();
|
|
353
|
+
const path = this.path();
|
|
354
|
+
this.uploading.set(true);
|
|
355
|
+
Promise.all(list.map((file) => this.uploader(file, { field, path })))
|
|
356
|
+
.then((refs) => this.setValue(refs))
|
|
357
|
+
.catch(() => { }) // host upload failure keeps the previous value
|
|
358
|
+
.finally(() => {
|
|
359
|
+
this.uploading.set(false);
|
|
360
|
+
this.changeDetector.markForCheck();
|
|
361
|
+
this.notify()();
|
|
362
|
+
});
|
|
363
|
+
}
|
|
364
|
+
/** Names shown under a `file` control (refs or plain names). */
|
|
365
|
+
fileNames() {
|
|
366
|
+
const value = this.value();
|
|
367
|
+
if (!Array.isArray(value))
|
|
368
|
+
return [];
|
|
369
|
+
return value.map((item) => typeof item === 'object' && item !== null
|
|
370
|
+
? String(item.name ?? '')
|
|
371
|
+
: String(item));
|
|
372
|
+
}
|
|
373
|
+
prefix() {
|
|
374
|
+
const value = this.node().props?.['prefix'];
|
|
375
|
+
return typeof value === 'string' && value ? value : null;
|
|
376
|
+
}
|
|
377
|
+
suffix() {
|
|
378
|
+
const value = this.node().props?.['suffix'];
|
|
379
|
+
return typeof value === 'string' && value ? value : null;
|
|
380
|
+
}
|
|
381
|
+
tagValues() {
|
|
382
|
+
const value = this.value();
|
|
383
|
+
return Array.isArray(value) ? value.map(String) : [];
|
|
384
|
+
}
|
|
385
|
+
addTag(event) {
|
|
386
|
+
event.preventDefault();
|
|
387
|
+
const input = event.target;
|
|
388
|
+
const tag = input.value.trim();
|
|
389
|
+
if (!tag)
|
|
390
|
+
return;
|
|
391
|
+
this.setValue([...this.tagValues(), tag]);
|
|
392
|
+
input.value = '';
|
|
393
|
+
}
|
|
394
|
+
removeTag(index) {
|
|
395
|
+
const next = [...this.tagValues()];
|
|
396
|
+
next.splice(index, 1);
|
|
397
|
+
this.setValue(next);
|
|
300
398
|
}
|
|
301
399
|
touch() {
|
|
302
400
|
this.engine().markTouched(this.path());
|
|
303
|
-
|
|
401
|
+
// Async-aware: resolves immediately for sync-only fields; pending events
|
|
402
|
+
// drive the busy indicator otherwise.
|
|
403
|
+
void this.engine().validateFieldAsync(this.path());
|
|
404
|
+
}
|
|
405
|
+
// -- reference (autocomplete) ---------------------------------------------
|
|
406
|
+
refOptions = signal([], ...(ngDevMode ? [{ debugName: "refOptions" }] : /* istanbul ignore next */ []));
|
|
407
|
+
refOpen = signal(false, ...(ngDevMode ? [{ debugName: "refOpen" }] : /* istanbul ignore next */ []));
|
|
408
|
+
refTimer = null;
|
|
409
|
+
/** Label of the chosen option (falls back to the raw value). */
|
|
410
|
+
refLabel = signal(null, ...(ngDevMode ? [{ debugName: "refLabel" }] : /* istanbul ignore next */ []));
|
|
411
|
+
refDisplay() {
|
|
412
|
+
return this.refLabel() ?? this.stringValue();
|
|
413
|
+
}
|
|
414
|
+
refSearch(event) {
|
|
415
|
+
const query = event.target.value;
|
|
416
|
+
this.refLabel.set(null);
|
|
417
|
+
if (this.refTimer)
|
|
418
|
+
clearTimeout(this.refTimer);
|
|
419
|
+
this.refTimer = setTimeout(() => this.refResolve(query), 250);
|
|
420
|
+
}
|
|
421
|
+
refResolve(query) {
|
|
422
|
+
const source = this.field().optionsSource;
|
|
423
|
+
if (!source || source.type !== 'provider') {
|
|
424
|
+
// static options filter locally
|
|
425
|
+
const filter = query.toLowerCase();
|
|
426
|
+
this.refOptions.set((this.field().options ?? []).filter((o) => o.label.toLowerCase().includes(filter)));
|
|
427
|
+
this.refOpen.set(true);
|
|
428
|
+
this.changeDetector.markForCheck();
|
|
429
|
+
this.notify()();
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
const provider = this.providers()[source.name];
|
|
433
|
+
if (!provider)
|
|
434
|
+
return;
|
|
435
|
+
Promise.resolve(provider({
|
|
436
|
+
field: this.field(),
|
|
437
|
+
path: this.path(),
|
|
438
|
+
data: this.engine().getData(),
|
|
439
|
+
args: source.args,
|
|
440
|
+
query,
|
|
441
|
+
}))
|
|
442
|
+
.then((options) => {
|
|
443
|
+
this.refOptions.set(options);
|
|
444
|
+
this.refOpen.set(true);
|
|
445
|
+
this.changeDetector.markForCheck();
|
|
446
|
+
this.notify()();
|
|
447
|
+
})
|
|
448
|
+
.catch(() => { });
|
|
449
|
+
}
|
|
450
|
+
refPick(option) {
|
|
451
|
+
this.setValue(option.value);
|
|
452
|
+
this.refLabel.set(option.label);
|
|
453
|
+
this.refOpen.set(false);
|
|
454
|
+
this.touch();
|
|
455
|
+
}
|
|
456
|
+
refBlur() {
|
|
457
|
+
// let a click on an option land before closing
|
|
458
|
+
setTimeout(() => {
|
|
459
|
+
this.refOpen.set(false);
|
|
460
|
+
this.changeDetector.markForCheck();
|
|
461
|
+
this.notify()();
|
|
462
|
+
}, 150);
|
|
463
|
+
this.touch();
|
|
304
464
|
}
|
|
305
465
|
addRow() {
|
|
306
466
|
this.engine().addRow(this.path());
|
|
@@ -309,18 +469,20 @@ class FormNodeComponent {
|
|
|
309
469
|
this.engine().removeRow(this.path(), index);
|
|
310
470
|
}
|
|
311
471
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: FormNodeComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
312
|
-
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: `
|
|
472
|
+
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: `
|
|
313
473
|
@let n = node();
|
|
314
474
|
@if (visible()) {
|
|
315
475
|
@switch (n.kind) {
|
|
316
476
|
@case ('static') {
|
|
317
477
|
@if (n.type === 'button') {
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
478
|
+
@if (interactive()) {
|
|
479
|
+
<button
|
|
480
|
+
class="mform-btn"
|
|
481
|
+
[type]="buttonAction() === 'submit' ? 'submit' : 'button'"
|
|
482
|
+
>
|
|
483
|
+
{{ n.label || 'Submit' }}
|
|
484
|
+
</button>
|
|
485
|
+
}
|
|
324
486
|
} @else if (n.type === 'content') {
|
|
325
487
|
<div class="mform-content" [innerHTML]="content()"></div>
|
|
326
488
|
} @else {
|
|
@@ -336,7 +498,7 @@ class FormNodeComponent {
|
|
|
336
498
|
@for (column of columns(); track $index) {
|
|
337
499
|
<div class="mform-column">
|
|
338
500
|
@for (child of column; track child.key) {
|
|
339
|
-
<mform-node [node]="child" [engine]="engine()" [tick]="tick()" [providers]="providers()" [scope]="childScope()" />
|
|
501
|
+
<mform-node [node]="child" [engine]="engine()" [tick]="tick()" [providers]="providers()" [mode]="mode()" [notify]="notify()" [scope]="childScope()" />
|
|
340
502
|
}
|
|
341
503
|
</div>
|
|
342
504
|
}
|
|
@@ -361,7 +523,7 @@ class FormNodeComponent {
|
|
|
361
523
|
@for (tab of container().children; track tab.key; let i = $index) {
|
|
362
524
|
@if (activeTab() === i) {
|
|
363
525
|
<div class="mform-tabpanel" role="tabpanel">
|
|
364
|
-
<mform-node [node]="tab" [engine]="engine()" [tick]="tick()" [providers]="providers()" [scope]="childScope()" [bare]="true" />
|
|
526
|
+
<mform-node [node]="tab" [engine]="engine()" [tick]="tick()" [providers]="providers()" [mode]="mode()" [notify]="notify()" [scope]="childScope()" [bare]="true" />
|
|
365
527
|
</div>
|
|
366
528
|
}
|
|
367
529
|
}
|
|
@@ -377,20 +539,24 @@ class FormNodeComponent {
|
|
|
377
539
|
<div class="mform-grid-row" role="group" [attr.aria-label]="(container().label || container().key) + ' ' + (i + 1)">
|
|
378
540
|
<div class="mform-grid-fields">
|
|
379
541
|
@for (child of container().children; track child.key) {
|
|
380
|
-
<mform-node [node]="child" [engine]="engine()" [tick]="tick()" [providers]="providers()" [scope]="rowScope(i)" />
|
|
542
|
+
<mform-node [node]="child" [engine]="engine()" [tick]="tick()" [providers]="providers()" [mode]="mode()" [notify]="notify()" [scope]="rowScope(i)" />
|
|
381
543
|
}
|
|
382
544
|
</div>
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
545
|
+
@if (interactive()) {
|
|
546
|
+
<button
|
|
547
|
+
type="button"
|
|
548
|
+
class="mform-row-remove"
|
|
549
|
+
(click)="removeRow(i)"
|
|
550
|
+
[attr.aria-label]="'Remove row ' + (i + 1)"
|
|
551
|
+
>
|
|
552
|
+
✕
|
|
553
|
+
</button>
|
|
554
|
+
}
|
|
391
555
|
</div>
|
|
392
556
|
}
|
|
393
|
-
|
|
557
|
+
@if (interactive()) {
|
|
558
|
+
<button type="button" class="mform-row-add" (click)="addRow()">+ Add</button>
|
|
559
|
+
}
|
|
394
560
|
</div>
|
|
395
561
|
</section>
|
|
396
562
|
}
|
|
@@ -400,14 +566,14 @@ class FormNodeComponent {
|
|
|
400
566
|
<header class="mform-panel-head">{{ container().label }}</header>
|
|
401
567
|
<div class="mform-panel-body">
|
|
402
568
|
@for (child of container().children; track child.key) {
|
|
403
|
-
<mform-node [node]="child" [engine]="engine()" [tick]="tick()" [providers]="providers()" [scope]="childScope()" />
|
|
569
|
+
<mform-node [node]="child" [engine]="engine()" [tick]="tick()" [providers]="providers()" [mode]="mode()" [notify]="notify()" [scope]="childScope()" />
|
|
404
570
|
}
|
|
405
571
|
</div>
|
|
406
572
|
</section>
|
|
407
573
|
} @else {
|
|
408
574
|
<div class="mform-group">
|
|
409
575
|
@for (child of container().children; track child.key) {
|
|
410
|
-
<mform-node [node]="child" [engine]="engine()" [tick]="tick()" [providers]="providers()" [scope]="childScope()" />
|
|
576
|
+
<mform-node [node]="child" [engine]="engine()" [tick]="tick()" [providers]="providers()" [mode]="mode()" [notify]="notify()" [scope]="childScope()" />
|
|
411
577
|
}
|
|
412
578
|
</div>
|
|
413
579
|
}
|
|
@@ -421,7 +587,7 @@ class FormNodeComponent {
|
|
|
421
587
|
[ngComponentOutletInputs]="customInputs()"
|
|
422
588
|
/>
|
|
423
589
|
} @else if (field().type !== 'hidden') {
|
|
424
|
-
<div class="mform-field" [class.has-error]="errors().length > 0">
|
|
590
|
+
<div class="mform-field" [class.has-error]="errors().length > 0" [class.is-pending]="pending()">
|
|
425
591
|
@if (field().type !== 'checkbox') {
|
|
426
592
|
<label class="mform-label" [attr.for]="controlId()">
|
|
427
593
|
{{ field().label || field().key }}
|
|
@@ -437,7 +603,7 @@ class FormNodeComponent {
|
|
|
437
603
|
[id]="controlId()"
|
|
438
604
|
[value]="stringValue()"
|
|
439
605
|
[placeholder]="field().placeholder || ''"
|
|
440
|
-
[disabled]="
|
|
606
|
+
[disabled]="controlDisabled()"
|
|
441
607
|
[attr.rows]="rowsProp()"
|
|
442
608
|
[attr.aria-required]="required() || null"
|
|
443
609
|
[attr.aria-invalid]="errors().length > 0 || null"
|
|
@@ -450,7 +616,7 @@ class FormNodeComponent {
|
|
|
450
616
|
<select
|
|
451
617
|
class="mform-control"
|
|
452
618
|
[id]="controlId()"
|
|
453
|
-
[disabled]="
|
|
619
|
+
[disabled]="controlDisabled()"
|
|
454
620
|
[attr.aria-required]="required() || null"
|
|
455
621
|
[attr.aria-invalid]="errors().length > 0 || null"
|
|
456
622
|
[attr.aria-describedby]="describedBy()"
|
|
@@ -476,7 +642,7 @@ class FormNodeComponent {
|
|
|
476
642
|
type="radio"
|
|
477
643
|
[name]="controlId()"
|
|
478
644
|
[checked]="stringValue() === '' + option.value"
|
|
479
|
-
[disabled]="
|
|
645
|
+
[disabled]="controlDisabled()"
|
|
480
646
|
(change)="setValue(option.value)"
|
|
481
647
|
(blur)="touch()"
|
|
482
648
|
/>
|
|
@@ -491,7 +657,7 @@ class FormNodeComponent {
|
|
|
491
657
|
type="checkbox"
|
|
492
658
|
[id]="controlId()"
|
|
493
659
|
[checked]="value() === true"
|
|
494
|
-
[disabled]="
|
|
660
|
+
[disabled]="controlDisabled()"
|
|
495
661
|
[attr.aria-invalid]="errors().length > 0 || null"
|
|
496
662
|
[attr.aria-describedby]="describedBy()"
|
|
497
663
|
(change)="setChecked($event)"
|
|
@@ -512,7 +678,7 @@ class FormNodeComponent {
|
|
|
512
678
|
<input
|
|
513
679
|
type="checkbox"
|
|
514
680
|
[checked]="mapValue()['' + option.value] === true"
|
|
515
|
-
[disabled]="
|
|
681
|
+
[disabled]="controlDisabled()"
|
|
516
682
|
(change)="toggleMap('' + option.value, $event)"
|
|
517
683
|
(blur)="touch()"
|
|
518
684
|
/>
|
|
@@ -521,17 +687,75 @@ class FormNodeComponent {
|
|
|
521
687
|
}
|
|
522
688
|
</div>
|
|
523
689
|
}
|
|
690
|
+
@case ('tags') {
|
|
691
|
+
<div class="mform-tags">
|
|
692
|
+
@for (tag of tagValues(); track $index; let i = $index) {
|
|
693
|
+
<span class="mform-tag">
|
|
694
|
+
{{ tag }}
|
|
695
|
+
<button
|
|
696
|
+
type="button"
|
|
697
|
+
(click)="removeTag(i)"
|
|
698
|
+
[attr.aria-label]="'Remove ' + tag"
|
|
699
|
+
>✕</button>
|
|
700
|
+
</span>
|
|
701
|
+
}
|
|
702
|
+
<input
|
|
703
|
+
class="mform-control mform-tag-input"
|
|
704
|
+
type="text"
|
|
705
|
+
[id]="controlId()"
|
|
706
|
+
[placeholder]="field().placeholder || ''"
|
|
707
|
+
[disabled]="controlDisabled()"
|
|
708
|
+
[attr.aria-describedby]="describedBy()"
|
|
709
|
+
(keydown.enter)="addTag($event)"
|
|
710
|
+
(blur)="touch()"
|
|
711
|
+
/>
|
|
712
|
+
</div>
|
|
713
|
+
}
|
|
524
714
|
@case ('file') {
|
|
525
715
|
<input
|
|
526
716
|
class="mform-control"
|
|
527
717
|
type="file"
|
|
528
718
|
multiple
|
|
529
719
|
[id]="controlId()"
|
|
530
|
-
[disabled]="
|
|
720
|
+
[disabled]="controlDisabled() || uploading()"
|
|
721
|
+
[attr.aria-busy]="uploading() || null"
|
|
531
722
|
[attr.aria-describedby]="describedBy()"
|
|
532
723
|
(change)="setFiles($event)"
|
|
533
724
|
(blur)="touch()"
|
|
534
725
|
/>
|
|
726
|
+
@if (uploading()) {
|
|
727
|
+
<div class="mform-desc" role="status">Uploading…</div>
|
|
728
|
+
}
|
|
729
|
+
@for (name of fileNames(); track $index) {
|
|
730
|
+
<div class="mform-file-name">📎 {{ name }}</div>
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
@case ('reference') {
|
|
734
|
+
<div class="mform-reference">
|
|
735
|
+
<input
|
|
736
|
+
class="mform-control"
|
|
737
|
+
type="text"
|
|
738
|
+
role="combobox"
|
|
739
|
+
[id]="controlId()"
|
|
740
|
+
[value]="refDisplay()"
|
|
741
|
+
[placeholder]="field().placeholder || ''"
|
|
742
|
+
[disabled]="controlDisabled()"
|
|
743
|
+
[attr.aria-expanded]="refOpen()"
|
|
744
|
+
[attr.aria-invalid]="errors().length > 0 || null"
|
|
745
|
+
[attr.aria-describedby]="describedBy()"
|
|
746
|
+
(input)="refSearch($event)"
|
|
747
|
+
(blur)="refBlur()"
|
|
748
|
+
/>
|
|
749
|
+
@if (refOpen() && refOptions().length > 0) {
|
|
750
|
+
<ul class="mform-ref-list" role="listbox">
|
|
751
|
+
@for (option of refOptions(); track option.value) {
|
|
752
|
+
<li role="option">
|
|
753
|
+
<button type="button" (click)="refPick(option)">{{ option.label }}</button>
|
|
754
|
+
</li>
|
|
755
|
+
}
|
|
756
|
+
</ul>
|
|
757
|
+
}
|
|
758
|
+
</div>
|
|
535
759
|
}
|
|
536
760
|
@case ('unsupported') {
|
|
537
761
|
<input
|
|
@@ -543,19 +767,29 @@ class FormNodeComponent {
|
|
|
543
767
|
/>
|
|
544
768
|
}
|
|
545
769
|
@default {
|
|
546
|
-
<
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
770
|
+
<div class="mform-affix" [class.has-affix]="prefix() || suffix()">
|
|
771
|
+
@if (prefix(); as p) {
|
|
772
|
+
<span class="mform-prefix" aria-hidden="true">{{ p }}</span>
|
|
773
|
+
}
|
|
774
|
+
<input
|
|
775
|
+
class="mform-control"
|
|
776
|
+
[type]="inputType()"
|
|
777
|
+
[id]="controlId()"
|
|
778
|
+
[value]="stringValue()"
|
|
779
|
+
[placeholder]="field().placeholder || ''"
|
|
780
|
+
[disabled]="controlDisabled()"
|
|
781
|
+
[attr.step]="field().type === 'currency' ? '0.01' : null"
|
|
782
|
+
[attr.inputmode]="field().type === 'currency' ? 'decimal' : null"
|
|
783
|
+
[attr.aria-required]="required() || null"
|
|
784
|
+
[attr.aria-invalid]="errors().length > 0 || null"
|
|
785
|
+
[attr.aria-describedby]="describedBy()"
|
|
786
|
+
(input)="setString($event)"
|
|
787
|
+
(blur)="touch()"
|
|
788
|
+
/>
|
|
789
|
+
@if (suffix(); as s) {
|
|
790
|
+
<span class="mform-suffix" aria-hidden="true">{{ s }}</span>
|
|
791
|
+
}
|
|
792
|
+
</div>
|
|
559
793
|
}
|
|
560
794
|
}
|
|
561
795
|
@if (field().description) {
|
|
@@ -569,7 +803,7 @@ class FormNodeComponent {
|
|
|
569
803
|
}
|
|
570
804
|
}
|
|
571
805
|
}
|
|
572
|
-
`, 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 });
|
|
806
|
+
`, 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 });
|
|
573
807
|
}
|
|
574
808
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: FormNodeComponent, decorators: [{
|
|
575
809
|
type: Component,
|
|
@@ -585,12 +819,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImpo
|
|
|
585
819
|
@switch (n.kind) {
|
|
586
820
|
@case ('static') {
|
|
587
821
|
@if (n.type === 'button') {
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
822
|
+
@if (interactive()) {
|
|
823
|
+
<button
|
|
824
|
+
class="mform-btn"
|
|
825
|
+
[type]="buttonAction() === 'submit' ? 'submit' : 'button'"
|
|
826
|
+
>
|
|
827
|
+
{{ n.label || 'Submit' }}
|
|
828
|
+
</button>
|
|
829
|
+
}
|
|
594
830
|
} @else if (n.type === 'content') {
|
|
595
831
|
<div class="mform-content" [innerHTML]="content()"></div>
|
|
596
832
|
} @else {
|
|
@@ -606,7 +842,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImpo
|
|
|
606
842
|
@for (column of columns(); track $index) {
|
|
607
843
|
<div class="mform-column">
|
|
608
844
|
@for (child of column; track child.key) {
|
|
609
|
-
<mform-node [node]="child" [engine]="engine()" [tick]="tick()" [providers]="providers()" [scope]="childScope()" />
|
|
845
|
+
<mform-node [node]="child" [engine]="engine()" [tick]="tick()" [providers]="providers()" [mode]="mode()" [notify]="notify()" [scope]="childScope()" />
|
|
610
846
|
}
|
|
611
847
|
</div>
|
|
612
848
|
}
|
|
@@ -631,7 +867,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImpo
|
|
|
631
867
|
@for (tab of container().children; track tab.key; let i = $index) {
|
|
632
868
|
@if (activeTab() === i) {
|
|
633
869
|
<div class="mform-tabpanel" role="tabpanel">
|
|
634
|
-
<mform-node [node]="tab" [engine]="engine()" [tick]="tick()" [providers]="providers()" [scope]="childScope()" [bare]="true" />
|
|
870
|
+
<mform-node [node]="tab" [engine]="engine()" [tick]="tick()" [providers]="providers()" [mode]="mode()" [notify]="notify()" [scope]="childScope()" [bare]="true" />
|
|
635
871
|
</div>
|
|
636
872
|
}
|
|
637
873
|
}
|
|
@@ -647,20 +883,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImpo
|
|
|
647
883
|
<div class="mform-grid-row" role="group" [attr.aria-label]="(container().label || container().key) + ' ' + (i + 1)">
|
|
648
884
|
<div class="mform-grid-fields">
|
|
649
885
|
@for (child of container().children; track child.key) {
|
|
650
|
-
<mform-node [node]="child" [engine]="engine()" [tick]="tick()" [providers]="providers()" [scope]="rowScope(i)" />
|
|
886
|
+
<mform-node [node]="child" [engine]="engine()" [tick]="tick()" [providers]="providers()" [mode]="mode()" [notify]="notify()" [scope]="rowScope(i)" />
|
|
651
887
|
}
|
|
652
888
|
</div>
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
889
|
+
@if (interactive()) {
|
|
890
|
+
<button
|
|
891
|
+
type="button"
|
|
892
|
+
class="mform-row-remove"
|
|
893
|
+
(click)="removeRow(i)"
|
|
894
|
+
[attr.aria-label]="'Remove row ' + (i + 1)"
|
|
895
|
+
>
|
|
896
|
+
✕
|
|
897
|
+
</button>
|
|
898
|
+
}
|
|
661
899
|
</div>
|
|
662
900
|
}
|
|
663
|
-
|
|
901
|
+
@if (interactive()) {
|
|
902
|
+
<button type="button" class="mform-row-add" (click)="addRow()">+ Add</button>
|
|
903
|
+
}
|
|
664
904
|
</div>
|
|
665
905
|
</section>
|
|
666
906
|
}
|
|
@@ -670,14 +910,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImpo
|
|
|
670
910
|
<header class="mform-panel-head">{{ container().label }}</header>
|
|
671
911
|
<div class="mform-panel-body">
|
|
672
912
|
@for (child of container().children; track child.key) {
|
|
673
|
-
<mform-node [node]="child" [engine]="engine()" [tick]="tick()" [providers]="providers()" [scope]="childScope()" />
|
|
913
|
+
<mform-node [node]="child" [engine]="engine()" [tick]="tick()" [providers]="providers()" [mode]="mode()" [notify]="notify()" [scope]="childScope()" />
|
|
674
914
|
}
|
|
675
915
|
</div>
|
|
676
916
|
</section>
|
|
677
917
|
} @else {
|
|
678
918
|
<div class="mform-group">
|
|
679
919
|
@for (child of container().children; track child.key) {
|
|
680
|
-
<mform-node [node]="child" [engine]="engine()" [tick]="tick()" [providers]="providers()" [scope]="childScope()" />
|
|
920
|
+
<mform-node [node]="child" [engine]="engine()" [tick]="tick()" [providers]="providers()" [mode]="mode()" [notify]="notify()" [scope]="childScope()" />
|
|
681
921
|
}
|
|
682
922
|
</div>
|
|
683
923
|
}
|
|
@@ -691,7 +931,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImpo
|
|
|
691
931
|
[ngComponentOutletInputs]="customInputs()"
|
|
692
932
|
/>
|
|
693
933
|
} @else if (field().type !== 'hidden') {
|
|
694
|
-
<div class="mform-field" [class.has-error]="errors().length > 0">
|
|
934
|
+
<div class="mform-field" [class.has-error]="errors().length > 0" [class.is-pending]="pending()">
|
|
695
935
|
@if (field().type !== 'checkbox') {
|
|
696
936
|
<label class="mform-label" [attr.for]="controlId()">
|
|
697
937
|
{{ field().label || field().key }}
|
|
@@ -707,7 +947,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImpo
|
|
|
707
947
|
[id]="controlId()"
|
|
708
948
|
[value]="stringValue()"
|
|
709
949
|
[placeholder]="field().placeholder || ''"
|
|
710
|
-
[disabled]="
|
|
950
|
+
[disabled]="controlDisabled()"
|
|
711
951
|
[attr.rows]="rowsProp()"
|
|
712
952
|
[attr.aria-required]="required() || null"
|
|
713
953
|
[attr.aria-invalid]="errors().length > 0 || null"
|
|
@@ -720,7 +960,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImpo
|
|
|
720
960
|
<select
|
|
721
961
|
class="mform-control"
|
|
722
962
|
[id]="controlId()"
|
|
723
|
-
[disabled]="
|
|
963
|
+
[disabled]="controlDisabled()"
|
|
724
964
|
[attr.aria-required]="required() || null"
|
|
725
965
|
[attr.aria-invalid]="errors().length > 0 || null"
|
|
726
966
|
[attr.aria-describedby]="describedBy()"
|
|
@@ -746,7 +986,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImpo
|
|
|
746
986
|
type="radio"
|
|
747
987
|
[name]="controlId()"
|
|
748
988
|
[checked]="stringValue() === '' + option.value"
|
|
749
|
-
[disabled]="
|
|
989
|
+
[disabled]="controlDisabled()"
|
|
750
990
|
(change)="setValue(option.value)"
|
|
751
991
|
(blur)="touch()"
|
|
752
992
|
/>
|
|
@@ -761,7 +1001,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImpo
|
|
|
761
1001
|
type="checkbox"
|
|
762
1002
|
[id]="controlId()"
|
|
763
1003
|
[checked]="value() === true"
|
|
764
|
-
[disabled]="
|
|
1004
|
+
[disabled]="controlDisabled()"
|
|
765
1005
|
[attr.aria-invalid]="errors().length > 0 || null"
|
|
766
1006
|
[attr.aria-describedby]="describedBy()"
|
|
767
1007
|
(change)="setChecked($event)"
|
|
@@ -782,7 +1022,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImpo
|
|
|
782
1022
|
<input
|
|
783
1023
|
type="checkbox"
|
|
784
1024
|
[checked]="mapValue()['' + option.value] === true"
|
|
785
|
-
[disabled]="
|
|
1025
|
+
[disabled]="controlDisabled()"
|
|
786
1026
|
(change)="toggleMap('' + option.value, $event)"
|
|
787
1027
|
(blur)="touch()"
|
|
788
1028
|
/>
|
|
@@ -791,17 +1031,75 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImpo
|
|
|
791
1031
|
}
|
|
792
1032
|
</div>
|
|
793
1033
|
}
|
|
1034
|
+
@case ('tags') {
|
|
1035
|
+
<div class="mform-tags">
|
|
1036
|
+
@for (tag of tagValues(); track $index; let i = $index) {
|
|
1037
|
+
<span class="mform-tag">
|
|
1038
|
+
{{ tag }}
|
|
1039
|
+
<button
|
|
1040
|
+
type="button"
|
|
1041
|
+
(click)="removeTag(i)"
|
|
1042
|
+
[attr.aria-label]="'Remove ' + tag"
|
|
1043
|
+
>✕</button>
|
|
1044
|
+
</span>
|
|
1045
|
+
}
|
|
1046
|
+
<input
|
|
1047
|
+
class="mform-control mform-tag-input"
|
|
1048
|
+
type="text"
|
|
1049
|
+
[id]="controlId()"
|
|
1050
|
+
[placeholder]="field().placeholder || ''"
|
|
1051
|
+
[disabled]="controlDisabled()"
|
|
1052
|
+
[attr.aria-describedby]="describedBy()"
|
|
1053
|
+
(keydown.enter)="addTag($event)"
|
|
1054
|
+
(blur)="touch()"
|
|
1055
|
+
/>
|
|
1056
|
+
</div>
|
|
1057
|
+
}
|
|
794
1058
|
@case ('file') {
|
|
795
1059
|
<input
|
|
796
1060
|
class="mform-control"
|
|
797
1061
|
type="file"
|
|
798
1062
|
multiple
|
|
799
1063
|
[id]="controlId()"
|
|
800
|
-
[disabled]="
|
|
1064
|
+
[disabled]="controlDisabled() || uploading()"
|
|
1065
|
+
[attr.aria-busy]="uploading() || null"
|
|
801
1066
|
[attr.aria-describedby]="describedBy()"
|
|
802
1067
|
(change)="setFiles($event)"
|
|
803
1068
|
(blur)="touch()"
|
|
804
1069
|
/>
|
|
1070
|
+
@if (uploading()) {
|
|
1071
|
+
<div class="mform-desc" role="status">Uploading…</div>
|
|
1072
|
+
}
|
|
1073
|
+
@for (name of fileNames(); track $index) {
|
|
1074
|
+
<div class="mform-file-name">📎 {{ name }}</div>
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
@case ('reference') {
|
|
1078
|
+
<div class="mform-reference">
|
|
1079
|
+
<input
|
|
1080
|
+
class="mform-control"
|
|
1081
|
+
type="text"
|
|
1082
|
+
role="combobox"
|
|
1083
|
+
[id]="controlId()"
|
|
1084
|
+
[value]="refDisplay()"
|
|
1085
|
+
[placeholder]="field().placeholder || ''"
|
|
1086
|
+
[disabled]="controlDisabled()"
|
|
1087
|
+
[attr.aria-expanded]="refOpen()"
|
|
1088
|
+
[attr.aria-invalid]="errors().length > 0 || null"
|
|
1089
|
+
[attr.aria-describedby]="describedBy()"
|
|
1090
|
+
(input)="refSearch($event)"
|
|
1091
|
+
(blur)="refBlur()"
|
|
1092
|
+
/>
|
|
1093
|
+
@if (refOpen() && refOptions().length > 0) {
|
|
1094
|
+
<ul class="mform-ref-list" role="listbox">
|
|
1095
|
+
@for (option of refOptions(); track option.value) {
|
|
1096
|
+
<li role="option">
|
|
1097
|
+
<button type="button" (click)="refPick(option)">{{ option.label }}</button>
|
|
1098
|
+
</li>
|
|
1099
|
+
}
|
|
1100
|
+
</ul>
|
|
1101
|
+
}
|
|
1102
|
+
</div>
|
|
805
1103
|
}
|
|
806
1104
|
@case ('unsupported') {
|
|
807
1105
|
<input
|
|
@@ -813,19 +1111,29 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImpo
|
|
|
813
1111
|
/>
|
|
814
1112
|
}
|
|
815
1113
|
@default {
|
|
816
|
-
<
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
1114
|
+
<div class="mform-affix" [class.has-affix]="prefix() || suffix()">
|
|
1115
|
+
@if (prefix(); as p) {
|
|
1116
|
+
<span class="mform-prefix" aria-hidden="true">{{ p }}</span>
|
|
1117
|
+
}
|
|
1118
|
+
<input
|
|
1119
|
+
class="mform-control"
|
|
1120
|
+
[type]="inputType()"
|
|
1121
|
+
[id]="controlId()"
|
|
1122
|
+
[value]="stringValue()"
|
|
1123
|
+
[placeholder]="field().placeholder || ''"
|
|
1124
|
+
[disabled]="controlDisabled()"
|
|
1125
|
+
[attr.step]="field().type === 'currency' ? '0.01' : null"
|
|
1126
|
+
[attr.inputmode]="field().type === 'currency' ? 'decimal' : null"
|
|
1127
|
+
[attr.aria-required]="required() || null"
|
|
1128
|
+
[attr.aria-invalid]="errors().length > 0 || null"
|
|
1129
|
+
[attr.aria-describedby]="describedBy()"
|
|
1130
|
+
(input)="setString($event)"
|
|
1131
|
+
(blur)="touch()"
|
|
1132
|
+
/>
|
|
1133
|
+
@if (suffix(); as s) {
|
|
1134
|
+
<span class="mform-suffix" aria-hidden="true">{{ s }}</span>
|
|
1135
|
+
}
|
|
1136
|
+
</div>
|
|
829
1137
|
}
|
|
830
1138
|
}
|
|
831
1139
|
@if (field().description) {
|
|
@@ -841,7 +1149,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImpo
|
|
|
841
1149
|
}
|
|
842
1150
|
`,
|
|
843
1151
|
}]
|
|
844
|
-
}], 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 }] }] } });
|
|
1152
|
+
}], 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 }] }] } });
|
|
845
1153
|
|
|
846
1154
|
/**
|
|
847
1155
|
* `<mform-renderer>` — renders a form at runtime from a `FormSchema` (or any
|
|
@@ -864,6 +1172,20 @@ class FormRendererComponent {
|
|
|
864
1172
|
optionsProviders = input({}, ...(ngDevMode ? [{ debugName: "optionsProviders" }] : /* istanbul ignore next */ []));
|
|
865
1173
|
/** Host-supplied message resolver (localization of validation messages). */
|
|
866
1174
|
messages = input(null, ...(ngDevMode ? [{ debugName: "messages" }] : /* istanbul ignore next */ []));
|
|
1175
|
+
/** Named custom validators (sync or async — Promises get pending states). */
|
|
1176
|
+
validators = input(null, ...(ngDevMode ? [{ debugName: "validators" }] : /* istanbul ignore next */ []));
|
|
1177
|
+
/** `edit` (default), `readonly` (no actions) or `disabled`. */
|
|
1178
|
+
mode = input('edit', ...(ngDevMode ? [{ debugName: "mode" }] : /* istanbul ignore next */ []));
|
|
1179
|
+
/**
|
|
1180
|
+
* Optional host submission: called after a VALID submit with the result;
|
|
1181
|
+
* rejections surface as the submission error state. Persistence transport
|
|
1182
|
+
* stays entirely on the host side.
|
|
1183
|
+
*/
|
|
1184
|
+
submitHandler = input(null, ...(ngDevMode ? [{ debugName: "submitHandler" }] : /* istanbul ignore next */ []));
|
|
1185
|
+
/** True while async validation or the submit handler is running. */
|
|
1186
|
+
submitting = signal(false, ...(ngDevMode ? [{ debugName: "submitting" }] : /* istanbul ignore next */ []));
|
|
1187
|
+
/** Message when the host submission rejected. */
|
|
1188
|
+
submitError = signal(null, ...(ngDevMode ? [{ debugName: "submitError" }] : /* istanbul ignore next */ []));
|
|
867
1189
|
/** Fixed renderer texts (override to localize). `{{count}}` in `summary`. */
|
|
868
1190
|
labels = input({
|
|
869
1191
|
previous: 'Previous',
|
|
@@ -886,6 +1208,8 @@ class FormRendererComponent {
|
|
|
886
1208
|
valueChanged = output();
|
|
887
1209
|
/** Bumped on every engine event; nodes read it to refresh. */
|
|
888
1210
|
tick = signal(0, ...(ngDevMode ? [{ debugName: "tick" }] : /* istanbul ignore next */ []));
|
|
1211
|
+
/** Handed to nodes so async completions re-render (zoneless-safe). */
|
|
1212
|
+
bumpTick = () => this.tick.update((v) => v + 1);
|
|
889
1213
|
/** Active wizard step index. */
|
|
890
1214
|
currentStep = signal(0, ...(ngDevMode ? [{ debugName: "currentStep" }] : /* istanbul ignore next */ []));
|
|
891
1215
|
resolved = computed(() => {
|
|
@@ -916,6 +1240,7 @@ class FormRendererComponent {
|
|
|
916
1240
|
? createFormEngine(schema, {
|
|
917
1241
|
initialData: this.initialData() ?? undefined,
|
|
918
1242
|
messages: this.messages() ?? undefined,
|
|
1243
|
+
validators: this.validators() ?? undefined,
|
|
919
1244
|
})
|
|
920
1245
|
: null;
|
|
921
1246
|
}, ...(ngDevMode ? [{ debugName: "engine" }] : /* istanbul ignore next */ []));
|
|
@@ -966,22 +1291,37 @@ class FormRendererComponent {
|
|
|
966
1291
|
this.currentStep.update((index) => Math.min(this.steps().length - 1, index + 1));
|
|
967
1292
|
}
|
|
968
1293
|
// -- submission -----------------------------------------------------------
|
|
969
|
-
onSubmit(event) {
|
|
1294
|
+
async onSubmit(event) {
|
|
970
1295
|
event.preventDefault();
|
|
971
1296
|
const engine = this.engine();
|
|
972
|
-
if (!engine)
|
|
1297
|
+
if (!engine || this.submitting() || this.mode() !== 'edit')
|
|
973
1298
|
return;
|
|
974
|
-
|
|
975
|
-
this.
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
1299
|
+
this.submitting.set(true);
|
|
1300
|
+
this.submitError.set(null);
|
|
1301
|
+
try {
|
|
1302
|
+
const result = await engine.submitAsync();
|
|
1303
|
+
this.errorSummary.set(result.errors);
|
|
1304
|
+
if (!result.ok) {
|
|
1305
|
+
if (this.isWizard()) {
|
|
1306
|
+
const stepIndex = this.stepIndexForPath(result.errors[0]?.path ?? '');
|
|
1307
|
+
if (stepIndex >= 0)
|
|
1308
|
+
this.currentStep.set(stepIndex);
|
|
1309
|
+
}
|
|
1310
|
+
this.focusFirstError(result.errors);
|
|
981
1311
|
}
|
|
982
|
-
this.
|
|
1312
|
+
else if (this.submitHandler()) {
|
|
1313
|
+
try {
|
|
1314
|
+
await this.submitHandler()(result);
|
|
1315
|
+
}
|
|
1316
|
+
catch (error) {
|
|
1317
|
+
this.submitError.set(String(error instanceof Error ? error.message : error));
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
this.submitted.emit(result);
|
|
1321
|
+
}
|
|
1322
|
+
finally {
|
|
1323
|
+
this.submitting.set(false);
|
|
983
1324
|
}
|
|
984
|
-
this.submitted.emit(result);
|
|
985
1325
|
}
|
|
986
1326
|
/** Finds the wizard step that owns a (possibly row-indexed) data path. */
|
|
987
1327
|
stepIndexForPath(path) {
|
|
@@ -1001,7 +1341,7 @@ class FormRendererComponent {
|
|
|
1001
1341
|
queueMicrotask(() => this.documentRef.getElementById(id)?.focus());
|
|
1002
1342
|
}
|
|
1003
1343
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: FormRendererComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1004
|
-
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: `
|
|
1344
|
+
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: `
|
|
1005
1345
|
@if (engine(); as engine) {
|
|
1006
1346
|
<form class="mform-root" novalidate (submit)="onSubmit($event)">
|
|
1007
1347
|
@if (errorSummary().length > 0) {
|
|
@@ -1034,6 +1374,7 @@ class FormRendererComponent {
|
|
|
1034
1374
|
[engine]="engine"
|
|
1035
1375
|
[tick]="tick()"
|
|
1036
1376
|
[providers]="optionsProviders()"
|
|
1377
|
+
[mode]="mode()" [notify]="bumpTick"
|
|
1037
1378
|
[bare]="true"
|
|
1038
1379
|
/>
|
|
1039
1380
|
}
|
|
@@ -1047,18 +1388,23 @@ class FormRendererComponent {
|
|
|
1047
1388
|
<button type="button" class="mform-btn" (click)="nextStep()">
|
|
1048
1389
|
{{ labels().next }}
|
|
1049
1390
|
</button>
|
|
1050
|
-
} @else {
|
|
1051
|
-
<button type="submit" class="mform-btn"
|
|
1391
|
+
} @else if (mode() === 'edit') {
|
|
1392
|
+
<button type="submit" class="mform-btn" [disabled]="submitting()">
|
|
1393
|
+
{{ submitting() ? '…' : labels().submit }}
|
|
1394
|
+
</button>
|
|
1052
1395
|
}
|
|
1053
1396
|
</div>
|
|
1054
1397
|
} @else {
|
|
1055
1398
|
@for (node of engine.schema.fields; track node.key) {
|
|
1056
|
-
<mform-node [node]="node" [engine]="engine" [tick]="tick()" [providers]="optionsProviders()" />
|
|
1399
|
+
<mform-node [node]="node" [engine]="engine" [tick]="tick()" [providers]="optionsProviders()" [mode]="mode()" [notify]="bumpTick" />
|
|
1057
1400
|
}
|
|
1058
1401
|
}
|
|
1402
|
+
@if (submitError(); as error) {
|
|
1403
|
+
<div class="mform-summary" role="alert">{{ error }}</div>
|
|
1404
|
+
}
|
|
1059
1405
|
</form>
|
|
1060
1406
|
}
|
|
1061
|
-
`, 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)}.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 });
|
|
1407
|
+
`, 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 });
|
|
1062
1408
|
}
|
|
1063
1409
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImport: i0, type: FormRendererComponent, decorators: [{
|
|
1064
1410
|
type: Component,
|
|
@@ -1095,6 +1441,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImpo
|
|
|
1095
1441
|
[engine]="engine"
|
|
1096
1442
|
[tick]="tick()"
|
|
1097
1443
|
[providers]="optionsProviders()"
|
|
1444
|
+
[mode]="mode()" [notify]="bumpTick"
|
|
1098
1445
|
[bare]="true"
|
|
1099
1446
|
/>
|
|
1100
1447
|
}
|
|
@@ -1108,19 +1455,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImpo
|
|
|
1108
1455
|
<button type="button" class="mform-btn" (click)="nextStep()">
|
|
1109
1456
|
{{ labels().next }}
|
|
1110
1457
|
</button>
|
|
1111
|
-
} @else {
|
|
1112
|
-
<button type="submit" class="mform-btn"
|
|
1458
|
+
} @else if (mode() === 'edit') {
|
|
1459
|
+
<button type="submit" class="mform-btn" [disabled]="submitting()">
|
|
1460
|
+
{{ submitting() ? '…' : labels().submit }}
|
|
1461
|
+
</button>
|
|
1113
1462
|
}
|
|
1114
1463
|
</div>
|
|
1115
1464
|
} @else {
|
|
1116
1465
|
@for (node of engine.schema.fields; track node.key) {
|
|
1117
|
-
<mform-node [node]="node" [engine]="engine" [tick]="tick()" [providers]="optionsProviders()" />
|
|
1466
|
+
<mform-node [node]="node" [engine]="engine" [tick]="tick()" [providers]="optionsProviders()" [mode]="mode()" [notify]="bumpTick" />
|
|
1118
1467
|
}
|
|
1119
1468
|
}
|
|
1469
|
+
@if (submitError(); as error) {
|
|
1470
|
+
<div class="mform-summary" role="alert">{{ error }}</div>
|
|
1471
|
+
}
|
|
1120
1472
|
</form>
|
|
1121
1473
|
}
|
|
1122
|
-
`, 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)}.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"] }]
|
|
1123
|
-
}], 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"] }] } });
|
|
1474
|
+
`, 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"] }]
|
|
1475
|
+
}], 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"] }] } });
|
|
1124
1476
|
|
|
1125
1477
|
/*
|
|
1126
1478
|
* Runtime entry point of @mosaicoo/form-angular.
|
|
@@ -1135,5 +1487,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.19", ngImpo
|
|
|
1135
1487
|
* Generated bundle index. Do not edit.
|
|
1136
1488
|
*/
|
|
1137
1489
|
|
|
1138
|
-
export { FormNodeComponent, FormRendererComponent, MFORM_FIELD_COMPONENTS, MFORM_REMOTE_FETCHER, provideMosaicooForm };
|
|
1490
|
+
export { FormNodeComponent, FormRendererComponent, MFORM_FIELD_COMPONENTS, MFORM_REMOTE_FETCHER, MFORM_UPLOAD, provideMosaicooForm };
|
|
1139
1491
|
//# sourceMappingURL=mosaicoo-form-angular.mjs.map
|