ngx-dynamic-entity 2.2.0 → 2.3.1
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 +76 -4
- package/fesm2022/ngx-dynamic-entity.mjs +474 -197
- package/fesm2022/ngx-dynamic-entity.mjs.map +1 -1
- package/package.json +6 -3
- package/types/ngx-dynamic-entity.d.ts +100 -14
|
@@ -2,7 +2,7 @@ import * as i0 from '@angular/core';
|
|
|
2
2
|
import { InjectionToken, makeEnvironmentProviders, inject, Injectable, signal, Input, ChangeDetectionStrategy, Component, ChangeDetectorRef, DestroyRef, Injector, isDevMode, ViewContainerRef, ViewChild, forwardRef, Optional, Inject, EventEmitter, ElementRef, computed, afterNextRender, HostListener, ViewChildren, Output } from '@angular/core';
|
|
3
3
|
import * as i1 from '@angular/forms';
|
|
4
4
|
import { ReactiveFormsModule, Validators, FormBuilder, FormGroup, FormArray, FormsModule } from '@angular/forms';
|
|
5
|
-
import { resolveLabel, formatDisplayValue, normalizeLookupValues, lookupValuesToOptions, valuesMatch, resolveOptionLabel, resolveOptionValue, buildReferenceCacheKey, normalizeReferenceOptions, entityKeyFromCacheKey, applyCascadeFilter, resolveEffectiveMask, shouldMaskField, findTab, isValidUrl, isValidPhone, fileRefName, sliderBounds, ratingScale, normalizeHexColor, normalizeTags, evaluateFormRules, filterRulesForTab, evaluateFieldVisibility, getTabPath, fieldRefFor,
|
|
5
|
+
import { resolveLabel, formatDisplayValue, normalizeLookupValues, lookupValuesToOptions, valuesMatch, resolveOptionLabel, resolveOptionValue, buildReferenceCacheKey, normalizeReferenceOptions, entityKeyFromCacheKey, applyCascadeFilter, resolveEffectiveMask, shouldMaskField, findTab, isValidUrl, isValidPhone, fileRefName, sliderBounds, ratingScale, normalizeHexColor, normalizeTags, evaluateFormRules, filterRulesForTab, evaluateFieldVisibility, fieldsUnderTab, getTabPath, fieldRefFor, flattenFieldValues, parseFieldRef, placeTabFields, normalizeArrayStructures, getTabData, getValueByPath, normalizeConfigOptions, migrateRecord, ambiguousFieldIds, toRefToken, collectFieldScopes, applyAutoPatch, refOf, isUnsafePath, valuesEqual, parseCsv, arrayBoundFor, deriveImportColumns, suggestMapping, cellText, applyMapping, decimalMarkFor, toCsv, stripIndices, CORE_VERSION, DEFAULT_ARRAY_ROWS, upgradeLegacyRefs, coerceCell, buildTemplateSpec, collectLeafTargets, formatConfigProblems } from '@dynamic-entity/core';
|
|
6
6
|
export { formatDisplayValue, normalizeConfig, normalizeOption, resolveLabel, resolveOptionLabel, resolveOptionValue, valuesMatch } from '@dynamic-entity/core';
|
|
7
7
|
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
8
8
|
import { Subject, isObservable, firstValueFrom } from 'rxjs';
|
|
@@ -406,10 +406,10 @@ class ValidationMessagesService {
|
|
|
406
406
|
return '';
|
|
407
407
|
return typeof message === 'function' ? message(language, error) : message;
|
|
408
408
|
}
|
|
409
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
410
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.
|
|
409
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: ValidationMessagesService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
410
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: ValidationMessagesService, providedIn: 'root' });
|
|
411
411
|
}
|
|
412
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
412
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: ValidationMessagesService, decorators: [{
|
|
413
413
|
type: Injectable,
|
|
414
414
|
args: [{ providedIn: 'root' }]
|
|
415
415
|
}] });
|
|
@@ -522,8 +522,8 @@ class TextFieldComponent {
|
|
|
522
522
|
return '';
|
|
523
523
|
return this.messages.resolveForField(this.control.errors, this.language, this.field.type);
|
|
524
524
|
}
|
|
525
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
526
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.
|
|
525
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: TextFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
526
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.25", type: TextFieldComponent, isStandalone: true, selector: "ngx-text-field", inputs: { field: "field", control: "control", language: "language", readonly: "readonly", masked: "masked" }, ngImport: i0, template: `
|
|
527
527
|
<div
|
|
528
528
|
class="ngx-field ngx-field--text"
|
|
529
529
|
[attr.data-testid]="'field-' + field.id"
|
|
@@ -583,7 +583,7 @@ class TextFieldComponent {
|
|
|
583
583
|
</div>
|
|
584
584
|
`, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
585
585
|
}
|
|
586
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
586
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: TextFieldComponent, decorators: [{
|
|
587
587
|
type: Component,
|
|
588
588
|
args: [{
|
|
589
589
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
@@ -698,8 +698,8 @@ class TextareaFieldComponent {
|
|
|
698
698
|
return '';
|
|
699
699
|
return this.messages.resolveForField(this.control.errors, this.language, this.field.type);
|
|
700
700
|
}
|
|
701
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
702
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.
|
|
701
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: TextareaFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
702
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.25", type: TextareaFieldComponent, isStandalone: true, selector: "ngx-textarea-field", inputs: { field: "field", control: "control", language: "language", readonly: "readonly", masked: "masked" }, ngImport: i0, template: `
|
|
703
703
|
<div
|
|
704
704
|
class="ngx-field ngx-field--textarea"
|
|
705
705
|
[attr.data-testid]="'field-' + field.id"
|
|
@@ -748,7 +748,7 @@ class TextareaFieldComponent {
|
|
|
748
748
|
</div>
|
|
749
749
|
`, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
750
750
|
}
|
|
751
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
751
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: TextareaFieldComponent, decorators: [{
|
|
752
752
|
type: Component,
|
|
753
753
|
args: [{
|
|
754
754
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
@@ -920,8 +920,16 @@ const DEFAULT_UI_TEXT = {
|
|
|
920
920
|
importGuessed: 'Guessed',
|
|
921
921
|
importMappedCount: '{mapped} of {total} field(s) matched.',
|
|
922
922
|
importRequiredUnmapped: '{count} required field(s) still need a column.',
|
|
923
|
+
importConstant: 'Fixed value: {value}',
|
|
924
|
+
importAddSlot: 'Add a {label} row',
|
|
925
|
+
importStaleHeading: 'Mapped to fields this form no longer has',
|
|
926
|
+
importStaleExplain: 'The import will refuse this mapping until these are removed.',
|
|
927
|
+
importRemoveEntry: 'Remove',
|
|
923
928
|
// Import wizard — review and result
|
|
924
929
|
importPreviewHeading: 'First {count} row(s), as they will be saved',
|
|
930
|
+
importDelimiterComma: 'Columns are separated by commas.',
|
|
931
|
+
importDelimiterSemicolon: 'Columns are separated by semicolons, and numbers use a decimal comma.',
|
|
932
|
+
importDelimiterTab: 'Columns are separated by tabs.',
|
|
925
933
|
importRowRejected: 'This row will not be imported: {reason}',
|
|
926
934
|
importRunImport: 'Import {count} row(s)',
|
|
927
935
|
importImporting: 'Importing…',
|
|
@@ -1022,10 +1030,10 @@ class UiTextService {
|
|
|
1022
1030
|
text(key, language = 'en', params) {
|
|
1023
1031
|
return resolveUiText(this.overrides, DEFAULT_UI_TEXT, key, language, params);
|
|
1024
1032
|
}
|
|
1025
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
1026
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.
|
|
1033
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: UiTextService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1034
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: UiTextService, providedIn: 'root' });
|
|
1027
1035
|
}
|
|
1028
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
1036
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: UiTextService, decorators: [{
|
|
1029
1037
|
type: Injectable,
|
|
1030
1038
|
args: [{ providedIn: 'root' }]
|
|
1031
1039
|
}] });
|
|
@@ -1152,8 +1160,8 @@ class MarkdownFieldComponent {
|
|
|
1152
1160
|
return '';
|
|
1153
1161
|
return this.messages.resolveForField(this.control.errors, this.language, this.field.type);
|
|
1154
1162
|
}
|
|
1155
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
1156
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.
|
|
1163
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: MarkdownFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1164
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.25", type: MarkdownFieldComponent, isStandalone: true, selector: "ngx-markdown-field", inputs: { field: "field", control: "control", language: "language", readonly: "readonly", masked: "masked" }, usesOnChanges: true, ngImport: i0, template: `
|
|
1157
1165
|
<div
|
|
1158
1166
|
class="ngx-field ngx-field--markdown"
|
|
1159
1167
|
[attr.data-testid]="'field-' + field.id"
|
|
@@ -1253,7 +1261,7 @@ class MarkdownFieldComponent {
|
|
|
1253
1261
|
</div>
|
|
1254
1262
|
`, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1255
1263
|
}
|
|
1256
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
1264
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: MarkdownFieldComponent, decorators: [{
|
|
1257
1265
|
type: Component,
|
|
1258
1266
|
args: [{
|
|
1259
1267
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
@@ -1408,8 +1416,8 @@ class NumberFieldComponent {
|
|
|
1408
1416
|
return '';
|
|
1409
1417
|
return this.messages.resolveForField(this.control.errors, this.language, this.field.type);
|
|
1410
1418
|
}
|
|
1411
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
1412
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.
|
|
1419
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: NumberFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1420
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.25", type: NumberFieldComponent, isStandalone: true, selector: "ngx-number-field", inputs: { field: "field", control: "control", language: "language", readonly: "readonly", masked: "masked" }, ngImport: i0, template: `
|
|
1413
1421
|
<div
|
|
1414
1422
|
class="ngx-field ngx-field--number"
|
|
1415
1423
|
[attr.data-testid]="'field-' + field.id"
|
|
@@ -1469,7 +1477,7 @@ class NumberFieldComponent {
|
|
|
1469
1477
|
</div>
|
|
1470
1478
|
`, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1471
1479
|
}
|
|
1472
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
1480
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: NumberFieldComponent, decorators: [{
|
|
1473
1481
|
type: Component,
|
|
1474
1482
|
args: [{
|
|
1475
1483
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
@@ -1597,8 +1605,8 @@ class CurrencyFieldComponent {
|
|
|
1597
1605
|
return '';
|
|
1598
1606
|
return this.messages.resolveForField(this.control.errors, this.language, this.field.type);
|
|
1599
1607
|
}
|
|
1600
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
1601
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.
|
|
1608
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: CurrencyFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1609
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.25", type: CurrencyFieldComponent, isStandalone: true, selector: "ngx-currency-field", inputs: { field: "field", control: "control", language: "language", readonly: "readonly", masked: "masked" }, ngImport: i0, template: `
|
|
1602
1610
|
<div
|
|
1603
1611
|
class="ngx-field ngx-field--currency"
|
|
1604
1612
|
[attr.data-testid]="'field-' + field.id"
|
|
@@ -1653,7 +1661,7 @@ class CurrencyFieldComponent {
|
|
|
1653
1661
|
</div>
|
|
1654
1662
|
`, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1655
1663
|
}
|
|
1656
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
1664
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: CurrencyFieldComponent, decorators: [{
|
|
1657
1665
|
type: Component,
|
|
1658
1666
|
args: [{
|
|
1659
1667
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
@@ -1764,8 +1772,8 @@ class EmailFieldComponent {
|
|
|
1764
1772
|
return '';
|
|
1765
1773
|
return this.messages.resolveForField(this.control.errors, this.language, this.field.type);
|
|
1766
1774
|
}
|
|
1767
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
1768
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.
|
|
1775
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: EmailFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1776
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.25", type: EmailFieldComponent, isStandalone: true, selector: "ngx-email-field", inputs: { field: "field", control: "control", language: "language", readonly: "readonly", masked: "masked" }, ngImport: i0, template: `
|
|
1769
1777
|
<div
|
|
1770
1778
|
class="ngx-field ngx-field--email"
|
|
1771
1779
|
[attr.data-testid]="'field-' + field.id"
|
|
@@ -1822,7 +1830,7 @@ class EmailFieldComponent {
|
|
|
1822
1830
|
</div>
|
|
1823
1831
|
`, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1824
1832
|
}
|
|
1825
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
1833
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: EmailFieldComponent, decorators: [{
|
|
1826
1834
|
type: Component,
|
|
1827
1835
|
args: [{
|
|
1828
1836
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
@@ -1941,8 +1949,8 @@ class PasswordFieldComponent {
|
|
|
1941
1949
|
return '';
|
|
1942
1950
|
return this.messages.resolveForField(this.control.errors, this.language, this.field.type);
|
|
1943
1951
|
}
|
|
1944
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
1945
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.
|
|
1952
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: PasswordFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1953
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.25", type: PasswordFieldComponent, isStandalone: true, selector: "ngx-password-field", inputs: { field: "field", control: "control", language: "language", readonly: "readonly", masked: "masked" }, ngImport: i0, template: `
|
|
1946
1954
|
<div
|
|
1947
1955
|
class="ngx-field ngx-field--password"
|
|
1948
1956
|
[attr.data-testid]="'field-' + field.id"
|
|
@@ -2002,7 +2010,7 @@ class PasswordFieldComponent {
|
|
|
2002
2010
|
</div>
|
|
2003
2011
|
`, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2004
2012
|
}
|
|
2005
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
2013
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: PasswordFieldComponent, decorators: [{
|
|
2006
2014
|
type: Component,
|
|
2007
2015
|
args: [{
|
|
2008
2016
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
@@ -2116,8 +2124,8 @@ class CheckboxFieldComponent {
|
|
|
2116
2124
|
return '';
|
|
2117
2125
|
return this.messages.resolveForField(this.control.errors, this.language, this.field.type);
|
|
2118
2126
|
}
|
|
2119
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
2120
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.
|
|
2127
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: CheckboxFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2128
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.25", type: CheckboxFieldComponent, isStandalone: true, selector: "ngx-checkbox-field", inputs: { field: "field", control: "control", language: "language", readonly: "readonly", masked: "masked" }, ngImport: i0, template: `
|
|
2121
2129
|
<div
|
|
2122
2130
|
class="ngx-field ngx-field--checkbox"
|
|
2123
2131
|
[attr.data-testid]="'field-' + field.id"
|
|
@@ -2173,7 +2181,7 @@ class CheckboxFieldComponent {
|
|
|
2173
2181
|
</div>
|
|
2174
2182
|
`, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2175
2183
|
}
|
|
2176
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
2184
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: CheckboxFieldComponent, decorators: [{
|
|
2177
2185
|
type: Component,
|
|
2178
2186
|
args: [{
|
|
2179
2187
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
@@ -2288,8 +2296,8 @@ class BooleanFieldComponent {
|
|
|
2288
2296
|
return '';
|
|
2289
2297
|
return this.messages.resolveForField(this.control.errors, this.language, this.field.type);
|
|
2290
2298
|
}
|
|
2291
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
2292
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.
|
|
2299
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: BooleanFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2300
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.25", type: BooleanFieldComponent, isStandalone: true, selector: "ngx-boolean-field", inputs: { field: "field", control: "control", language: "language", readonly: "readonly", masked: "masked" }, ngImport: i0, template: `
|
|
2293
2301
|
<div
|
|
2294
2302
|
class="ngx-field ngx-field--boolean"
|
|
2295
2303
|
[attr.data-testid]="'field-' + field.id"
|
|
@@ -2353,7 +2361,7 @@ class BooleanFieldComponent {
|
|
|
2353
2361
|
</div>
|
|
2354
2362
|
`, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2355
2363
|
}
|
|
2356
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
2364
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: BooleanFieldComponent, decorators: [{
|
|
2357
2365
|
type: Component,
|
|
2358
2366
|
args: [{
|
|
2359
2367
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
@@ -2490,8 +2498,8 @@ class DateFieldComponent {
|
|
|
2490
2498
|
return '';
|
|
2491
2499
|
return this.messages.resolveForField(this.control.errors, this.language, this.field.type);
|
|
2492
2500
|
}
|
|
2493
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
2494
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.
|
|
2501
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: DateFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2502
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.25", type: DateFieldComponent, isStandalone: true, selector: "ngx-date-field", inputs: { field: "field", control: "control", language: "language", readonly: "readonly", masked: "masked" }, ngImport: i0, template: `
|
|
2495
2503
|
<div
|
|
2496
2504
|
class="ngx-field ngx-field--date"
|
|
2497
2505
|
[attr.data-testid]="'field-' + field.id"
|
|
@@ -2541,7 +2549,7 @@ class DateFieldComponent {
|
|
|
2541
2549
|
</div>
|
|
2542
2550
|
`, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2543
2551
|
}
|
|
2544
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
2552
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: DateFieldComponent, decorators: [{
|
|
2545
2553
|
type: Component,
|
|
2546
2554
|
args: [{
|
|
2547
2555
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
@@ -2710,8 +2718,8 @@ class DateTimeFieldComponent {
|
|
|
2710
2718
|
return '';
|
|
2711
2719
|
return this.messages.resolveForField(this.control.errors, this.language, this.field.type);
|
|
2712
2720
|
}
|
|
2713
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
2714
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.
|
|
2721
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: DateTimeFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2722
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.25", type: DateTimeFieldComponent, isStandalone: true, selector: "ngx-date-time-field", inputs: { field: "field", control: "control", language: "language", readonly: "readonly", masked: "masked" }, ngImport: i0, template: `
|
|
2715
2723
|
<div
|
|
2716
2724
|
class="ngx-field ngx-field--datetime"
|
|
2717
2725
|
[attr.data-testid]="'field-' + field.id"
|
|
@@ -2762,7 +2770,7 @@ class DateTimeFieldComponent {
|
|
|
2762
2770
|
</div>
|
|
2763
2771
|
`, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2764
2772
|
}
|
|
2765
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
2773
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: DateTimeFieldComponent, decorators: [{
|
|
2766
2774
|
type: Component,
|
|
2767
2775
|
args: [{
|
|
2768
2776
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
@@ -2881,8 +2889,8 @@ class TimeFieldComponent {
|
|
|
2881
2889
|
return '';
|
|
2882
2890
|
return this.messages.resolveForField(this.control.errors, this.language, this.field.type);
|
|
2883
2891
|
}
|
|
2884
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
2885
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.
|
|
2892
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: TimeFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2893
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.25", type: TimeFieldComponent, isStandalone: true, selector: "ngx-time-field", inputs: { field: "field", control: "control", language: "language", readonly: "readonly", masked: "masked" }, ngImport: i0, template: `
|
|
2886
2894
|
<div
|
|
2887
2895
|
class="ngx-field ngx-field--time"
|
|
2888
2896
|
[attr.data-testid]="'field-' + field.id"
|
|
@@ -2932,7 +2940,7 @@ class TimeFieldComponent {
|
|
|
2932
2940
|
</div>
|
|
2933
2941
|
`, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
2934
2942
|
}
|
|
2935
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
2943
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: TimeFieldComponent, decorators: [{
|
|
2936
2944
|
type: Component,
|
|
2937
2945
|
args: [{
|
|
2938
2946
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
@@ -3084,8 +3092,8 @@ class MonthYearFieldComponent {
|
|
|
3084
3092
|
return '';
|
|
3085
3093
|
return this.messages.resolveForField(this.control.errors, this.language, this.field.type);
|
|
3086
3094
|
}
|
|
3087
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
3088
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.
|
|
3095
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: MonthYearFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3096
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.25", type: MonthYearFieldComponent, isStandalone: true, selector: "ngx-month-year-field", inputs: { field: "field", control: "control", language: "language", readonly: "readonly", masked: "masked" }, ngImport: i0, template: `
|
|
3089
3097
|
<div
|
|
3090
3098
|
class="ngx-field ngx-field--month-year"
|
|
3091
3099
|
[attr.data-testid]="'field-' + field.id"
|
|
@@ -3164,7 +3172,7 @@ class MonthYearFieldComponent {
|
|
|
3164
3172
|
</div>
|
|
3165
3173
|
`, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
3166
3174
|
}
|
|
3167
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
3175
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: MonthYearFieldComponent, decorators: [{
|
|
3168
3176
|
type: Component,
|
|
3169
3177
|
args: [{
|
|
3170
3178
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
@@ -3465,10 +3473,10 @@ class LookupRegistryService {
|
|
|
3465
3473
|
value !== null &&
|
|
3466
3474
|
typeof value.subscribe === 'function');
|
|
3467
3475
|
}
|
|
3468
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
3469
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.
|
|
3476
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: LookupRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
3477
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: LookupRegistryService, providedIn: 'root' });
|
|
3470
3478
|
}
|
|
3471
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
3479
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: LookupRegistryService, decorators: [{
|
|
3472
3480
|
type: Injectable,
|
|
3473
3481
|
args: [{ providedIn: 'root' }]
|
|
3474
3482
|
}] });
|
|
@@ -3594,8 +3602,8 @@ class DropdownFieldComponent {
|
|
|
3594
3602
|
// dropdown than "This field is required", and both stay independently overridable.
|
|
3595
3603
|
return this.messages.resolveForField(this.control.errors, this.language, this.field.type);
|
|
3596
3604
|
}
|
|
3597
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
3598
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.
|
|
3605
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: DropdownFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3606
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.25", type: DropdownFieldComponent, isStandalone: true, selector: "ngx-dropdown-field", inputs: { field: "field", language: "language", control: "control", readonly: "readonly", masked: "masked" }, ngImport: i0, template: `
|
|
3599
3607
|
<div
|
|
3600
3608
|
class="ngx-field ngx-field--dropdown"
|
|
3601
3609
|
[attr.data-testid]="'field-' + field.id"
|
|
@@ -3665,7 +3673,7 @@ class DropdownFieldComponent {
|
|
|
3665
3673
|
</div>
|
|
3666
3674
|
`, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
3667
3675
|
}
|
|
3668
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
3676
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: DropdownFieldComponent, decorators: [{
|
|
3669
3677
|
type: Component,
|
|
3670
3678
|
args: [{
|
|
3671
3679
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
@@ -3880,8 +3888,8 @@ class RadioFieldComponent {
|
|
|
3880
3888
|
return '';
|
|
3881
3889
|
return this.messages.resolveForField(this.control.errors, this.language, this.field.type);
|
|
3882
3890
|
}
|
|
3883
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
3884
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.
|
|
3891
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: RadioFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3892
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.25", type: RadioFieldComponent, isStandalone: true, selector: "ngx-radio-field", inputs: { field: "field", language: "language", control: "control", readonly: "readonly", masked: "masked" }, ngImport: i0, template: `
|
|
3885
3893
|
<div
|
|
3886
3894
|
class="ngx-field ngx-field--radio"
|
|
3887
3895
|
[attr.data-testid]="'field-' + field.id"
|
|
@@ -3945,7 +3953,7 @@ class RadioFieldComponent {
|
|
|
3945
3953
|
</div>
|
|
3946
3954
|
`, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
3947
3955
|
}
|
|
3948
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
3956
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: RadioFieldComponent, decorators: [{
|
|
3949
3957
|
type: Component,
|
|
3950
3958
|
args: [{
|
|
3951
3959
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
@@ -4111,8 +4119,8 @@ class MultiSelectFieldComponent {
|
|
|
4111
4119
|
return '';
|
|
4112
4120
|
return this.messages.resolveForField(this.control.errors, this.language, this.field.type);
|
|
4113
4121
|
}
|
|
4114
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
4115
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.
|
|
4122
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: MultiSelectFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4123
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.25", type: MultiSelectFieldComponent, isStandalone: true, selector: "ngx-multi-select-field", inputs: { field: "field", language: "language", control: "control", readonly: "readonly", masked: "masked" }, ngImport: i0, template: `
|
|
4116
4124
|
<div
|
|
4117
4125
|
class="ngx-field ngx-field--multiSelect"
|
|
4118
4126
|
[attr.data-testid]="'field-' + field.id"
|
|
@@ -4172,7 +4180,7 @@ class MultiSelectFieldComponent {
|
|
|
4172
4180
|
</div>
|
|
4173
4181
|
`, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.SelectMultipleControlValueAccessor, selector: "select[multiple][formControlName],select[multiple][formControl],select[multiple][ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
4174
4182
|
}
|
|
4175
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
4183
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: MultiSelectFieldComponent, decorators: [{
|
|
4176
4184
|
type: Component,
|
|
4177
4185
|
args: [{
|
|
4178
4186
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
@@ -4271,10 +4279,10 @@ class EntityRefRegistryService {
|
|
|
4271
4279
|
has(entityKey) {
|
|
4272
4280
|
return this.registry.has(entityKey);
|
|
4273
4281
|
}
|
|
4274
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
4275
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.
|
|
4282
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: EntityRefRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
4283
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: EntityRefRegistryService, providedIn: 'root' });
|
|
4276
4284
|
}
|
|
4277
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
4285
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: EntityRefRegistryService, decorators: [{
|
|
4278
4286
|
type: Injectable,
|
|
4279
4287
|
args: [{ providedIn: 'root' }]
|
|
4280
4288
|
}] });
|
|
@@ -4434,10 +4442,10 @@ class EntityReferenceService {
|
|
|
4434
4442
|
matches(a, b, lang = 'en') {
|
|
4435
4443
|
return valuesMatch(a, b, lang);
|
|
4436
4444
|
}
|
|
4437
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
4438
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.
|
|
4445
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: EntityReferenceService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
4446
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: EntityReferenceService, providedIn: 'root' });
|
|
4439
4447
|
}
|
|
4440
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
4448
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: EntityReferenceService, decorators: [{
|
|
4441
4449
|
type: Injectable,
|
|
4442
4450
|
args: [{ providedIn: 'root' }]
|
|
4443
4451
|
}] });
|
|
@@ -4558,10 +4566,10 @@ class CascadeDataService {
|
|
|
4558
4566
|
walkTabs(config?.tabs);
|
|
4559
4567
|
return found;
|
|
4560
4568
|
}
|
|
4561
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
4562
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.
|
|
4569
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: CascadeDataService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
4570
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: CascadeDataService, providedIn: 'root' });
|
|
4563
4571
|
}
|
|
4564
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
4572
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: CascadeDataService, decorators: [{
|
|
4565
4573
|
type: Injectable,
|
|
4566
4574
|
args: [{ providedIn: 'root' }]
|
|
4567
4575
|
}] });
|
|
@@ -4582,10 +4590,10 @@ class EntityRefSelectionService {
|
|
|
4582
4590
|
emit(fieldId, option) {
|
|
4583
4591
|
this.subject.next({ fieldId, option });
|
|
4584
4592
|
}
|
|
4585
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
4586
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.
|
|
4593
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: EntityRefSelectionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
4594
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: EntityRefSelectionService, providedIn: 'root' });
|
|
4587
4595
|
}
|
|
4588
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
4596
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: EntityRefSelectionService, decorators: [{
|
|
4589
4597
|
type: Injectable,
|
|
4590
4598
|
args: [{ providedIn: 'root' }]
|
|
4591
4599
|
}] });
|
|
@@ -4713,8 +4721,8 @@ class EntityRefFieldComponent {
|
|
|
4713
4721
|
return '';
|
|
4714
4722
|
return this.messages.resolveForField(this.control.errors, this.language, this.field.type);
|
|
4715
4723
|
}
|
|
4716
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
4717
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.
|
|
4724
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: EntityRefFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4725
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.25", type: EntityRefFieldComponent, isStandalone: true, selector: "ngx-entity-ref-field", inputs: { field: "field", control: "control", language: "language", readonly: "readonly", masked: "masked" }, ngImport: i0, template: `
|
|
4718
4726
|
<div
|
|
4719
4727
|
class="ngx-field ngx-field--entity-ref"
|
|
4720
4728
|
[attr.data-testid]="'field-' + field.id"
|
|
@@ -4779,7 +4787,7 @@ class EntityRefFieldComponent {
|
|
|
4779
4787
|
</div>
|
|
4780
4788
|
`, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
4781
4789
|
}
|
|
4782
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
4790
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: EntityRefFieldComponent, decorators: [{
|
|
4783
4791
|
type: Component,
|
|
4784
4792
|
args: [{
|
|
4785
4793
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
@@ -4907,10 +4915,10 @@ class FieldRegistryService {
|
|
|
4907
4915
|
registeredTypes() {
|
|
4908
4916
|
return [...new Set([...this.dynamicRegistry.keys(), ...this.consumerRegistry.keys()])].sort();
|
|
4909
4917
|
}
|
|
4910
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
4911
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.
|
|
4918
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: FieldRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
4919
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: FieldRegistryService, providedIn: 'root' });
|
|
4912
4920
|
}
|
|
4913
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
4921
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: FieldRegistryService, decorators: [{
|
|
4914
4922
|
type: Injectable,
|
|
4915
4923
|
args: [{ providedIn: 'root' }]
|
|
4916
4924
|
}], ctorParameters: () => [] });
|
|
@@ -4949,10 +4957,10 @@ class RbacService {
|
|
|
4949
4957
|
canDelete: this.hasPermission(userRoles, config.permissions?.delete),
|
|
4950
4958
|
};
|
|
4951
4959
|
}
|
|
4952
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
4953
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.
|
|
4960
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: RbacService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
4961
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: RbacService, providedIn: 'root' });
|
|
4954
4962
|
}
|
|
4955
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
4963
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: RbacService, decorators: [{
|
|
4956
4964
|
type: Injectable,
|
|
4957
4965
|
args: [{ providedIn: 'root' }]
|
|
4958
4966
|
}] });
|
|
@@ -5074,10 +5082,10 @@ class DynamicFieldComponent {
|
|
|
5074
5082
|
ref.setInput('readonly', this.readonly || !!this.field.readonly);
|
|
5075
5083
|
ref.setInput('masked', masked);
|
|
5076
5084
|
}
|
|
5077
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
5078
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.
|
|
5085
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: DynamicFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
5086
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.25", type: DynamicFieldComponent, isStandalone: true, selector: "ngx-dynamic-field", inputs: { field: "field", control: "control", config: "config", currentTabId: "currentTabId", language: "language", readonly: "readonly", userRoles: "userRoles" }, viewQueries: [{ propertyName: "fieldHost", first: true, predicate: ["fieldHost"], descendants: true, read: ViewContainerRef, static: true }], usesOnChanges: true, ngImport: i0, template: `<ng-container #fieldHost></ng-container>`, isInline: true, styles: [":host{display:block;width:100%;min-width:0}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
5079
5087
|
}
|
|
5080
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
5088
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: DynamicFieldComponent, decorators: [{
|
|
5081
5089
|
type: Component,
|
|
5082
5090
|
args: [{ changeDetection: ChangeDetectionStrategy.OnPush, selector: 'ngx-dynamic-field', standalone: true, template: `<ng-container #fieldHost></ng-container>`, styles: [":host{display:block;width:100%;min-width:0}\n"] }]
|
|
5083
5091
|
}], propDecorators: { field: [{
|
|
@@ -5121,8 +5129,8 @@ class GroupFieldComponent {
|
|
|
5121
5129
|
const group = this.control;
|
|
5122
5130
|
return group?.get(childId);
|
|
5123
5131
|
}
|
|
5124
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
5125
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.
|
|
5132
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: GroupFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
5133
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.25", type: GroupFieldComponent, isStandalone: true, selector: "ngx-group-field", inputs: { field: "field", control: "control", config: "config", language: "language", readonly: "readonly", masked: "masked", userRoles: "userRoles" }, ngImport: i0, template: `
|
|
5126
5134
|
<fieldset
|
|
5127
5135
|
class="ngx-field ngx-field--group"
|
|
5128
5136
|
[attr.data-testid]="'field-' + field.id"
|
|
@@ -5163,7 +5171,7 @@ class GroupFieldComponent {
|
|
|
5163
5171
|
</fieldset>
|
|
5164
5172
|
`, isInline: true, styles: [".ngx-field--group{border:1px solid #e2e8f0;border-radius:8px;padding:16px;margin-bottom:16px;background:#fafafa}.ngx-field__legend{font-weight:600;font-size:14px;padding:0 8px;color:#334155}.ngx-field__group-children{display:flex;flex-direction:column;gap:12px}\n"], dependencies: [{ kind: "ngmodule", type: i0.forwardRef(() => ReactiveFormsModule) }, { kind: "component", type: i0.forwardRef(() => DynamicFieldComponent), selector: "ngx-dynamic-field", inputs: ["field", "control", "config", "currentTabId", "language", "readonly", "userRoles"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
5165
5173
|
}
|
|
5166
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
5174
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: GroupFieldComponent, decorators: [{
|
|
5167
5175
|
type: Component,
|
|
5168
5176
|
args: [{ changeDetection: ChangeDetectionStrategy.OnPush, selector: 'ngx-group-field', standalone: true, imports: [ReactiveFormsModule, forwardRef(() => DynamicFieldComponent)], template: `
|
|
5169
5177
|
<fieldset
|
|
@@ -5371,10 +5379,10 @@ class ValidatorRegistryService {
|
|
|
5371
5379
|
this.warned.add(id);
|
|
5372
5380
|
console.warn(`[ngx-dynamic-entity] ${message}`);
|
|
5373
5381
|
}
|
|
5374
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
5375
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.
|
|
5382
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: ValidatorRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
5383
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: ValidatorRegistryService, providedIn: 'root' });
|
|
5376
5384
|
}
|
|
5377
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
5385
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: ValidatorRegistryService, decorators: [{
|
|
5378
5386
|
type: Injectable,
|
|
5379
5387
|
args: [{ providedIn: 'root' }]
|
|
5380
5388
|
}] });
|
|
@@ -5427,8 +5435,8 @@ class ArrayFieldComponent {
|
|
|
5427
5435
|
this.formArray.removeAt(index);
|
|
5428
5436
|
this.cdr.markForCheck();
|
|
5429
5437
|
}
|
|
5430
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
5431
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.
|
|
5438
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: ArrayFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
5439
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.25", type: ArrayFieldComponent, isStandalone: true, selector: "ngx-array-field", inputs: { field: "field", control: "control", config: "config", language: "language", readonly: "readonly", masked: "masked", userRoles: "userRoles" }, ngImport: i0, template: `
|
|
5432
5440
|
<div
|
|
5433
5441
|
class="ngx-field ngx-field--array"
|
|
5434
5442
|
[attr.data-testid]="'field-' + field.id"
|
|
@@ -5507,7 +5515,7 @@ class ArrayFieldComponent {
|
|
|
5507
5515
|
</div>
|
|
5508
5516
|
`, isInline: true, styles: [".ngx-field--array{border:1px solid #e2e8f0;border-radius:8px;padding:16px;margin-bottom:16px;background:#f8fafc}.ngx-field__array-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:12px}.ngx-field__array-add-btn{background:#2563eb;color:#fff;border:none;padding:6px 12px;border-radius:4px;font-weight:500;cursor:pointer}.ngx-field__array-add-btn:hover{background:#1d4ed8}.ngx-field__array-list{display:flex;flex-direction:column;gap:12px}.ngx-field__array-item{background:#fff;border:1px solid #cbd5e1;border-radius:6px;padding:12px}.ngx-field__array-item-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:8px;border-bottom:1px solid #f1f5f9;padding-bottom:6px}.ngx-field__array-item-title{font-weight:600;font-size:13px;color:#475569}.ngx-field__array-remove-btn{background:#ef4444;color:#fff;border:none;padding:4px 8px;border-radius:4px;font-size:12px;cursor:pointer}.ngx-field__array-empty{color:#94a3b8;font-style:italic;font-size:13px}\n"], dependencies: [{ kind: "ngmodule", type: i0.forwardRef(() => ReactiveFormsModule) }, { kind: "component", type: i0.forwardRef(() => DynamicFieldComponent), selector: "ngx-dynamic-field", inputs: ["field", "control", "config", "currentTabId", "language", "readonly", "userRoles"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
5509
5517
|
}
|
|
5510
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
5518
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: ArrayFieldComponent, decorators: [{
|
|
5511
5519
|
type: Component,
|
|
5512
5520
|
args: [{ changeDetection: ChangeDetectionStrategy.OnPush, selector: 'ngx-array-field', standalone: true, imports: [ReactiveFormsModule, forwardRef(() => DynamicFieldComponent)], template: `
|
|
5513
5521
|
<div
|
|
@@ -5681,10 +5689,10 @@ class FileUploadService {
|
|
|
5681
5689
|
value !== null &&
|
|
5682
5690
|
typeof value.subscribe === 'function');
|
|
5683
5691
|
}
|
|
5684
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
5685
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.
|
|
5692
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: FileUploadService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
5693
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: FileUploadService, providedIn: 'root' });
|
|
5686
5694
|
}
|
|
5687
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
5695
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: FileUploadService, decorators: [{
|
|
5688
5696
|
type: Injectable,
|
|
5689
5697
|
args: [{ providedIn: 'root' }]
|
|
5690
5698
|
}] });
|
|
@@ -5769,8 +5777,8 @@ class ImageFieldComponent {
|
|
|
5769
5777
|
return '';
|
|
5770
5778
|
return this.messages.resolveForField(this.control.errors, this.language, this.field.type);
|
|
5771
5779
|
}
|
|
5772
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
5773
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.
|
|
5780
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: ImageFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
5781
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.25", type: ImageFieldComponent, isStandalone: true, selector: "ngx-image-field", inputs: { field: "field", control: "control", language: "language", readonly: "readonly", masked: "masked" }, ngImport: i0, template: `
|
|
5774
5782
|
<div
|
|
5775
5783
|
class="ngx-field ngx-field--image"
|
|
5776
5784
|
[attr.data-testid]="'field-' + field.id"
|
|
@@ -5849,7 +5857,7 @@ class ImageFieldComponent {
|
|
|
5849
5857
|
</div>
|
|
5850
5858
|
`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
5851
5859
|
}
|
|
5852
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
5860
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: ImageFieldComponent, decorators: [{
|
|
5853
5861
|
type: Component,
|
|
5854
5862
|
args: [{
|
|
5855
5863
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
@@ -6019,8 +6027,8 @@ class FileFieldComponent {
|
|
|
6019
6027
|
return '';
|
|
6020
6028
|
return this.messages.resolveForField(this.control.errors, this.language, this.field.type);
|
|
6021
6029
|
}
|
|
6022
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
6023
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.
|
|
6030
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: FileFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
6031
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.25", type: FileFieldComponent, isStandalone: true, selector: "ngx-file-field", inputs: { field: "field", control: "control", language: "language", readonly: "readonly", masked: "masked" }, ngImport: i0, template: `
|
|
6024
6032
|
<div
|
|
6025
6033
|
class="ngx-field ngx-field--file"
|
|
6026
6034
|
[attr.data-testid]="'field-' + field.id"
|
|
@@ -6102,7 +6110,7 @@ class FileFieldComponent {
|
|
|
6102
6110
|
</div>
|
|
6103
6111
|
`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
6104
6112
|
}
|
|
6105
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
6113
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: FileFieldComponent, decorators: [{
|
|
6106
6114
|
type: Component,
|
|
6107
6115
|
args: [{
|
|
6108
6116
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
@@ -6252,8 +6260,8 @@ class UrlFieldComponent {
|
|
|
6252
6260
|
return '';
|
|
6253
6261
|
return this.messages.resolveForField(this.control.errors, this.language, this.field.type);
|
|
6254
6262
|
}
|
|
6255
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
6256
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.
|
|
6263
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: UrlFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
6264
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.25", type: UrlFieldComponent, isStandalone: true, selector: "ngx-url-field", inputs: { field: "field", control: "control", language: "language", readonly: "readonly", masked: "masked" }, ngImport: i0, template: `
|
|
6257
6265
|
<div
|
|
6258
6266
|
class="ngx-field ngx-field--url"
|
|
6259
6267
|
[attr.data-testid]="'field-' + field.id"
|
|
@@ -6323,7 +6331,7 @@ class UrlFieldComponent {
|
|
|
6323
6331
|
</div>
|
|
6324
6332
|
`, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
6325
6333
|
}
|
|
6326
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
6334
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: UrlFieldComponent, decorators: [{
|
|
6327
6335
|
type: Component,
|
|
6328
6336
|
args: [{
|
|
6329
6337
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
@@ -6462,8 +6470,8 @@ class PhoneFieldComponent {
|
|
|
6462
6470
|
return '';
|
|
6463
6471
|
return this.messages.resolveForField(this.control.errors, this.language, this.field.type);
|
|
6464
6472
|
}
|
|
6465
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
6466
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.
|
|
6473
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: PhoneFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
6474
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.25", type: PhoneFieldComponent, isStandalone: true, selector: "ngx-phone-field", inputs: { field: "field", control: "control", language: "language", readonly: "readonly", masked: "masked" }, ngImport: i0, template: `
|
|
6467
6475
|
<div
|
|
6468
6476
|
class="ngx-field ngx-field--phone"
|
|
6469
6477
|
[attr.data-testid]="'field-' + field.id"
|
|
@@ -6531,7 +6539,7 @@ class PhoneFieldComponent {
|
|
|
6531
6539
|
</div>
|
|
6532
6540
|
`, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
6533
6541
|
}
|
|
6534
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
6542
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: PhoneFieldComponent, decorators: [{
|
|
6535
6543
|
type: Component,
|
|
6536
6544
|
args: [{
|
|
6537
6545
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
@@ -6670,8 +6678,8 @@ class SliderFieldComponent {
|
|
|
6670
6678
|
return '';
|
|
6671
6679
|
return this.messages.resolveForField(this.control.errors, this.language, this.field.type);
|
|
6672
6680
|
}
|
|
6673
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
6674
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.
|
|
6681
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: SliderFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
6682
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.25", type: SliderFieldComponent, isStandalone: true, selector: "ngx-slider-field", inputs: { field: "field", control: "control", language: "language", readonly: "readonly", masked: "masked" }, ngImport: i0, template: `
|
|
6675
6683
|
<div
|
|
6676
6684
|
class="ngx-field ngx-field--slider"
|
|
6677
6685
|
[attr.data-testid]="'field-' + field.id"
|
|
@@ -6744,7 +6752,7 @@ class SliderFieldComponent {
|
|
|
6744
6752
|
</div>
|
|
6745
6753
|
`, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.RangeValueAccessor, selector: "input[type=range][formControlName],input[type=range][formControl],input[type=range][ngModel]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
6746
6754
|
}
|
|
6747
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
6755
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: SliderFieldComponent, decorators: [{
|
|
6748
6756
|
type: Component,
|
|
6749
6757
|
args: [{
|
|
6750
6758
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
@@ -6921,8 +6929,8 @@ class RatingFieldComponent {
|
|
|
6921
6929
|
return '';
|
|
6922
6930
|
return this.messages.resolveForField(this.control.errors, this.language, this.field.type);
|
|
6923
6931
|
}
|
|
6924
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
6925
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.
|
|
6932
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: RatingFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
6933
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.25", type: RatingFieldComponent, isStandalone: true, selector: "ngx-rating-field", inputs: { field: "field", language: "language", readonly: "readonly", masked: "masked", control: "control" }, ngImport: i0, template: `
|
|
6926
6934
|
<div
|
|
6927
6935
|
class="ngx-field ngx-field--rating"
|
|
6928
6936
|
[attr.data-testid]="'field-' + field.id"
|
|
@@ -7019,7 +7027,7 @@ class RatingFieldComponent {
|
|
|
7019
7027
|
</div>
|
|
7020
7028
|
`, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
7021
7029
|
}
|
|
7022
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
7030
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: RatingFieldComponent, decorators: [{
|
|
7023
7031
|
type: Component,
|
|
7024
7032
|
args: [{
|
|
7025
7033
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
@@ -7188,8 +7196,8 @@ class ColorFieldComponent {
|
|
|
7188
7196
|
return '';
|
|
7189
7197
|
return this.messages.resolveForField(this.control.errors, this.language, this.field.type);
|
|
7190
7198
|
}
|
|
7191
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
7192
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.
|
|
7199
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: ColorFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
7200
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.25", type: ColorFieldComponent, isStandalone: true, selector: "ngx-color-field", inputs: { field: "field", control: "control", language: "language", readonly: "readonly", masked: "masked" }, ngImport: i0, template: `
|
|
7193
7201
|
<div
|
|
7194
7202
|
class="ngx-field ngx-field--color"
|
|
7195
7203
|
[attr.data-testid]="'field-' + field.id"
|
|
@@ -7269,7 +7277,7 @@ class ColorFieldComponent {
|
|
|
7269
7277
|
</div>
|
|
7270
7278
|
`, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
7271
7279
|
}
|
|
7272
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
7280
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: ColorFieldComponent, decorators: [{
|
|
7273
7281
|
type: Component,
|
|
7274
7282
|
args: [{
|
|
7275
7283
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
@@ -7484,8 +7492,8 @@ class TagsFieldComponent {
|
|
|
7484
7492
|
return '';
|
|
7485
7493
|
return this.messages.resolveForField(this.control.errors, this.language, this.field.type);
|
|
7486
7494
|
}
|
|
7487
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
7488
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.
|
|
7495
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: TagsFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
7496
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.25", type: TagsFieldComponent, isStandalone: true, selector: "ngx-tags-field", inputs: { field: "field", language: "language", readonly: "readonly", masked: "masked", control: "control" }, ngImport: i0, template: `
|
|
7489
7497
|
<div
|
|
7490
7498
|
class="ngx-field ngx-field--tags"
|
|
7491
7499
|
[attr.data-testid]="'field-' + field.id"
|
|
@@ -7575,7 +7583,7 @@ class TagsFieldComponent {
|
|
|
7575
7583
|
</div>
|
|
7576
7584
|
`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
7577
7585
|
}
|
|
7578
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
7586
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: TagsFieldComponent, decorators: [{
|
|
7579
7587
|
type: Component,
|
|
7580
7588
|
args: [{
|
|
7581
7589
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
@@ -7758,10 +7766,10 @@ class HookRegistryService {
|
|
|
7758
7766
|
return data;
|
|
7759
7767
|
return hook(data, context);
|
|
7760
7768
|
}
|
|
7761
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
7762
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.
|
|
7769
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: HookRegistryService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
7770
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: HookRegistryService, providedIn: 'root' });
|
|
7763
7771
|
}
|
|
7764
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
7772
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: HookRegistryService, decorators: [{
|
|
7765
7773
|
type: Injectable,
|
|
7766
7774
|
args: [{ providedIn: 'root' }]
|
|
7767
7775
|
}] });
|
|
@@ -7825,6 +7833,12 @@ class RulesEvaluationService {
|
|
|
7825
7833
|
* The predicate is `isFieldVisible`, the same one the render filter uses, so what is on
|
|
7826
7834
|
* screen and what counts toward validity cannot drift apart.
|
|
7827
7835
|
*
|
|
7836
|
+
* A tab that is not rendered — hidden by a rule, or `visibility: false` with no rule showing
|
|
7837
|
+
* it — hides everything it owns, sub-tabs and container children included. Only fields were
|
|
7838
|
+
* checked here, so a rule hiding a tab with a required field on it left the form invalid
|
|
7839
|
+
* with the field out of sight and out of the error summary: Save did nothing, silently.
|
|
7840
|
+
* Import already relaxed a hidden tab's fields, so the form refused rows import accepted.
|
|
7841
|
+
*
|
|
7828
7842
|
* Entries must arrive parent-first, which is what `collectFieldScopes` yields: a container's
|
|
7829
7843
|
* children are skipped once the container itself is hidden, because Angular's `disable()`
|
|
7830
7844
|
* cascades to descendants and a child's `enable()` walks back up recalculating ancestors.
|
|
@@ -7832,6 +7846,7 @@ class RulesEvaluationService {
|
|
|
7832
7846
|
*/
|
|
7833
7847
|
syncHiddenFieldState(entries, ctx) {
|
|
7834
7848
|
const hiddenContainers = [];
|
|
7849
|
+
const onHiddenTab = this.fieldsOnHiddenTabs(ctx.result, ctx.config);
|
|
7835
7850
|
for (const entry of entries) {
|
|
7836
7851
|
const field = entry.field;
|
|
7837
7852
|
if (!field?.id)
|
|
@@ -7839,7 +7854,7 @@ class RulesEvaluationService {
|
|
|
7839
7854
|
const address = ctx.addressOf(field);
|
|
7840
7855
|
if (hiddenContainers.some(prefix => address.startsWith(`${prefix}.`)))
|
|
7841
7856
|
continue;
|
|
7842
|
-
const hidden = !this.isFieldVisible(ctx.result, field, ctx.namesOf(field), ctx.values);
|
|
7857
|
+
const hidden = onHiddenTab.has(field) || !this.isFieldVisible(ctx.result, field, ctx.namesOf(field), ctx.values);
|
|
7843
7858
|
if (hidden && (field.type === 'group' || field.type === 'array'))
|
|
7844
7859
|
hiddenContainers.push(address);
|
|
7845
7860
|
const ctrl = ctx.controlFor(entry);
|
|
@@ -7854,6 +7869,30 @@ class RulesEvaluationService {
|
|
|
7854
7869
|
}
|
|
7855
7870
|
}
|
|
7856
7871
|
}
|
|
7872
|
+
/**
|
|
7873
|
+
* Every field owned by a tab that does not render, under `isTabVisible`'s precedence.
|
|
7874
|
+
*
|
|
7875
|
+
* Stops descending at the first hidden tab: `fieldsUnderTab` already covers its sub-tabs,
|
|
7876
|
+
* and a sub-tab of a hidden tab is not rendered whatever its own visibility says.
|
|
7877
|
+
*/
|
|
7878
|
+
fieldsOnHiddenTabs(result, config) {
|
|
7879
|
+
const out = new Set();
|
|
7880
|
+
const visit = (tabs) => {
|
|
7881
|
+
for (const tab of Array.isArray(tabs) ? tabs : []) {
|
|
7882
|
+
if (!tab || typeof tab !== 'object' || !tab.id)
|
|
7883
|
+
continue;
|
|
7884
|
+
if (this.isTabVisible(result, tab)) {
|
|
7885
|
+
visit(tab.children);
|
|
7886
|
+
}
|
|
7887
|
+
else {
|
|
7888
|
+
for (const entry of fieldsUnderTab(config, tab.id))
|
|
7889
|
+
out.add(entry.field);
|
|
7890
|
+
}
|
|
7891
|
+
}
|
|
7892
|
+
};
|
|
7893
|
+
visit(config?.tabs);
|
|
7894
|
+
return out;
|
|
7895
|
+
}
|
|
7857
7896
|
/**
|
|
7858
7897
|
* The hidden and shown names as sets, memoised against the result they came from.
|
|
7859
7898
|
*
|
|
@@ -7870,10 +7909,10 @@ class RulesEvaluationService {
|
|
|
7870
7909
|
return sets;
|
|
7871
7910
|
}
|
|
7872
7911
|
nameSetCache = new WeakMap();
|
|
7873
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
7874
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.
|
|
7912
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: RulesEvaluationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
7913
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: RulesEvaluationService, providedIn: 'root' });
|
|
7875
7914
|
}
|
|
7876
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
7915
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: RulesEvaluationService, decorators: [{
|
|
7877
7916
|
type: Injectable,
|
|
7878
7917
|
args: [{ providedIn: 'root' }]
|
|
7879
7918
|
}] });
|
|
@@ -7928,10 +7967,10 @@ class ConfigSourceService {
|
|
|
7928
7967
|
this.cache.clear();
|
|
7929
7968
|
}
|
|
7930
7969
|
}
|
|
7931
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
7932
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.
|
|
7970
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: ConfigSourceService, deps: [{ token: CONFIG_SOURCE, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
7971
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: ConfigSourceService, providedIn: 'root' });
|
|
7933
7972
|
}
|
|
7934
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
7973
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: ConfigSourceService, decorators: [{
|
|
7935
7974
|
type: Injectable,
|
|
7936
7975
|
args: [{ providedIn: 'root' }]
|
|
7937
7976
|
}], ctorParameters: () => [{ type: undefined, decorators: [{
|
|
@@ -8063,18 +8102,11 @@ class FormStructureService {
|
|
|
8063
8102
|
* unknown>` and needs no knowledge of refs at all — the extra keys simply resolve.
|
|
8064
8103
|
*/
|
|
8065
8104
|
flattenValues(form, entries) {
|
|
8066
|
-
|
|
8067
|
-
|
|
8068
|
-
const field = entry.field;
|
|
8069
|
-
if (!field?.id)
|
|
8070
|
-
continue;
|
|
8105
|
+
// The naming rule is core's, shared with the importer; only where values come from is ours.
|
|
8106
|
+
return flattenFieldValues(entries, entry => {
|
|
8071
8107
|
const ctrl = this.controlAt(form, entry);
|
|
8072
|
-
|
|
8073
|
-
|
|
8074
|
-
out[field.id] = ctrl.value;
|
|
8075
|
-
out[toRefToken(refOf(field, entry.scope))] = ctrl.value;
|
|
8076
|
-
}
|
|
8077
|
-
return out;
|
|
8108
|
+
return ctrl ? { value: ctrl.value } : undefined;
|
|
8109
|
+
});
|
|
8078
8110
|
}
|
|
8079
8111
|
/**
|
|
8080
8112
|
* Find a control by reference: a bracketed path, or a bare field id.
|
|
@@ -8129,14 +8161,8 @@ class FormStructureService {
|
|
|
8129
8161
|
if (ctrl)
|
|
8130
8162
|
fieldValBag[field.id] = ctrl.value;
|
|
8131
8163
|
}
|
|
8132
|
-
|
|
8133
|
-
|
|
8134
|
-
if (field.refererField) {
|
|
8135
|
-
const ctrl = this.getControl(form, config, field.id, tab.id);
|
|
8136
|
-
if (ctrl)
|
|
8137
|
-
setValueByPath(record, field.refererField, ctrl.value);
|
|
8138
|
-
}
|
|
8139
|
-
}
|
|
8164
|
+
// At its position and at its `refererField` — the placement import uses too.
|
|
8165
|
+
placeTabFields(record, tab, fieldValBag, config);
|
|
8140
8166
|
if (tab.children)
|
|
8141
8167
|
walkTabs(tab.children);
|
|
8142
8168
|
}
|
|
@@ -8219,10 +8245,10 @@ class FormStructureService {
|
|
|
8219
8245
|
fieldsById.has(key) &&
|
|
8220
8246
|
!patchedFieldIds.has(key));
|
|
8221
8247
|
}
|
|
8222
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
8223
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.
|
|
8248
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: FormStructureService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
8249
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: FormStructureService });
|
|
8224
8250
|
}
|
|
8225
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
8251
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: FormStructureService, decorators: [{
|
|
8226
8252
|
type: Injectable
|
|
8227
8253
|
}] });
|
|
8228
8254
|
|
|
@@ -9235,6 +9261,7 @@ class DynamicFormComponent {
|
|
|
9235
9261
|
namesOf: field => this.namesOf(field),
|
|
9236
9262
|
addressOf: field => this.addressOf(field),
|
|
9237
9263
|
controlFor: entry => this.controlForEntry(entry),
|
|
9264
|
+
config: this.config,
|
|
9238
9265
|
});
|
|
9239
9266
|
}
|
|
9240
9267
|
/** Every field in the config, including `group`/`array` children. */
|
|
@@ -9384,10 +9411,10 @@ class DynamicFormComponent {
|
|
|
9384
9411
|
this.submitAttempted.set(false);
|
|
9385
9412
|
this.formReset.emit();
|
|
9386
9413
|
}
|
|
9387
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
9388
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.24", type: DynamicFormComponent, isStandalone: true, selector: "ngx-dynamic-form", inputs: { config: "config", rules: "rules", initialData: "initialData", originalData: "originalData", userRoles: "userRoles", language: "language", readonly: "readonly", readOnlyFields: "readOnlyFields", showInfoBanners: "showInfoBanners", loading: "loading", error: "error", changeDebounceMs: "changeDebounceMs", preview: "preview", layout: "layout" }, outputs: { formSubmit: "formSubmit", formChange: "formChange", formReset: "formReset", activeTabChange: "activeTabChange", saveRejected: "saveRejected" }, host: { listeners: { "window:keydown": "handleKeyboardEvent($event)" } }, providers: [EntityRefSelectionService, FormStructureService], viewQueries: [{ propertyName: "formPanel", first: true, predicate: ["formPanel"], descendants: true }, { propertyName: "fieldHosts", predicate: DynamicFieldComponent, descendants: true }], usesOnChanges: true, ngImport: i0, template: "@if (canView) {\n <form [formGroup]=\"form\" (ngSubmit)=\"submit()\" class=\"ngx-form\" [class.ngx-form--readonly]=\"readonly\" novalidate>\n <!-- Error message banner -->\n @if (error) {\n <div class=\"ngx-form__error-banner\" role=\"alert\" data-testid=\"form-error\">{{ error }}</div>\n }\n\n <!--\n Why the last Save did nothing.\n Only after a refused submit \u2014 see `submitAttempted`. Each entry jumps to its field,\n switching tabs on the way, because the field is usually not the one on screen.\n -->\n @if (submitAttempted() && invalidFields().length > 0) {\n <div class=\"ngx-form__error-summary\" role=\"alert\" data-testid=\"error-summary\" tabindex=\"-1\">\n <!--\n `error-summary-heading`, not `-title`: the links below are `error-summary-<fieldId>`,\n and a field whose id happens to be `title` would then share a test id with this line.\n The `extensions` entity has exactly such a field.\n -->\n <p class=\"ngx-form__error-summary-title\" data-testid=\"error-summary-heading\">\n {{ ui.text('errorSummaryTitle', language, { count: invalidFields().length }) }}\n </p>\n <ul class=\"ngx-form__error-summary-list\">\n @for (entry of invalidFields(); track entry.field.id) {\n <li>\n <!--\n The field, and what is wrong with it \u2014 the same sentence the field renders\n under itself, so the summary is not a second opinion. Naming only the field\n left the user to visit each one to find out what it wanted.\n -->\n <button\n type=\"button\"\n class=\"ngx-form__error-summary-link\"\n [attr.data-testid]=\"'error-summary-' + entry.field.id\"\n (click)=\"jumpToField(entry.field.id)\"\n >\n <span class=\"ngx-form__error-summary-field\">{{ resolveFieldLabel(entry.field) }}</span>\n @if (entry.message) {\n <span class=\"ngx-form__error-summary-reason\">{{ entry.message }}</span>\n }\n </button>\n </li>\n }\n </ul>\n </div>\n }\n\n <!-- Rules Info Banners -->\n @for (infoKey of showInfoBanners ? Object.keys(ruleResult().infoBanners) : []; track infoKey) {\n <div class=\"ngx-form__info-banner\" role=\"status\" [attr.data-testid]=\"'info-banner-' + infoKey\">\n \u2139\uFE0F {{ ruleResult().infoBanners[infoKey] }}\n </div>\n }\n\n <!-- Rules Validation Errors & Warnings -->\n @for (errKey of Object.keys(ruleResult().validationErrors); track errKey) {\n <div class=\"ngx-form__error-banner\" role=\"alert\" [attr.data-testid]=\"'rule-error-' + errKey\">\n \u26A0\uFE0F {{ ruleResult().validationErrors[errKey] }}\n </div>\n }\n @for (warnKey of Object.keys(ruleResult().validationWarnings); track warnKey) {\n <div class=\"ngx-form__warning-banner\" role=\"status\" [attr.data-testid]=\"'rule-warning-' + warnKey\">\n \u26A1 {{ ruleResult().validationWarnings[warnKey] }}\n </div>\n }\n\n <!-- Deferred VALUE_CHANGED notice for criticalField edits (vs session baseline) -->\n @if (changedCriticalFields().length > 0) {\n <div class=\"ngx-form__warning-banner\" role=\"status\" data-testid=\"critical-change-banner\">\n {{ ui.text('criticalFieldChanged', language, { fields: changedCriticalFieldLabels() }) }}\n </div>\n }\n\n <!-- Primary Tab bar \u2014 only if visible tabs defined -->\n @if (visibleTabs.length > 0) {\n <div class=\"ngx-form__tabs\" role=\"tablist\" data-testid=\"tab-strip\">\n @for (tab of visibleTabs; track tab.id) {\n <button\n type=\"button\"\n class=\"ngx-form__tab\"\n [attr.data-testid]=\"'tab-' + tab.id\"\n [class.ngx-form__tab--active]=\"activeTab() === tab.id\"\n (click)=\"setActiveTab(tab.id)\"\n role=\"tab\"\n [attr.aria-selected]=\"activeTab() === tab.id\"\n >\n {{ resolveTabLabel(tab) }}\n <!--\n How many fields on this tab are stopping the save.\n The count is in the accessible name rather than the badge, because \"3\" read out\n after a tab name says nothing about what it counts.\n -->\n @if (tabErrorCount(tab); as errors) {\n <span\n class=\"ngx-form__tab-badge\"\n [attr.data-testid]=\"'tab-errors-' + tab.id\"\n [attr.aria-label]=\"ui.text('tabErrorCount', language, { count: errors })\"\n >{{ errors }}</span\n >\n }\n </button>\n }\n </div>\n }\n\n <!-- Sub-tab bar \u2014 only if active tab has sub-tabs -->\n @if (visibleSubTabs.length > 0) {\n <div\n class=\"ngx-form__tabs ngx-form__tabs--sub\"\n role=\"tablist\"\n data-testid=\"subtab-strip\"\n style=\"margin-top: -12px; margin-bottom: 12px; font-size: 13px\"\n >\n @for (subTab of visibleSubTabs; track subTab.id) {\n <button\n type=\"button\"\n class=\"ngx-form__tab ngx-form__tab--sub\"\n [attr.data-testid]=\"'subtab-' + subTab.id\"\n [class.ngx-form__tab--active]=\"activeSubTabConfig?.id === subTab.id\"\n (click)=\"setActiveSubTab(subTab.id)\"\n role=\"tab\"\n [attr.aria-selected]=\"activeSubTabConfig?.id === subTab.id\"\n >\n {{ resolveTabLabel(subTab) }}\n @if (subTabErrorCount(subTab); as errors) {\n <span\n class=\"ngx-form__tab-badge\"\n [attr.data-testid]=\"'subtab-errors-' + subTab.id\"\n [attr.aria-label]=\"ui.text('tabErrorCount', language, { count: errors })\"\n >{{ errors }}</span\n >\n }\n </button>\n }\n </div>\n }\n\n <!--\n One tabpanel per tab, wrapping both branches. Focus moves here on a tab switch, and a\n module tab is still a tab \u2014 putting the panel semantics on only the fields branch left\n a module tab with nowhere for focus to land.\n -->\n <div role=\"tabpanel\" tabindex=\"-1\" #formPanel class=\"ngx-form__tabpanel\" [attr.aria-label]=\"activeTabLabel\">\n @if (activeTabModuleComponent) {\n <div class=\"ngx-form__module-panel\" data-testid=\"module-panel\">\n <ng-container\n *ngComponentOutlet=\"activeTabModuleComponent; inputs: (activeSubTabConfig || activeTabConfig)?.moduleInputs\"\n ></ng-container>\n </div>\n } @else {\n <!-- Fields for active tab (Responsive 12-column grid) -->\n <div class=\"ngx-form__panel\" data-testid=\"form-panel\">\n @for (field of fieldsForActiveTab; track field.id) {\n <div\n class=\"ngx-form__field-slot\"\n [style.gridColumn]=\"getFieldSpan(field)\"\n [attr.id]=\"'field-container-' + field.id\"\n tabindex=\"-1\"\n >\n <ngx-dynamic-field\n [field]=\"field\"\n [control]=\"getControl(field.id, (activeSubTabConfig || activeTabConfig)?.id)!\"\n [config]=\"config\"\n [currentTabId]=\"(activeSubTabConfig || activeTabConfig)?.id\"\n [language]=\"language\"\n [readonly]=\"isFieldReadonly(field)\"\n [userRoles]=\"userRoles\"\n />\n @if (field.hasDrift) {\n <!--\n Drift was previously visible only in the builder, so a referenced field whose\n source had changed looked completely normal to the person filling the form in.\n role=\"status\" rather than \"alert\": it is information, not an error.\n -->\n <span\n class=\"ngx-field__drift\"\n role=\"status\"\n [attr.data-testid]=\"'field-' + field.id + '-drift'\"\n [title]=\"driftHint(field)\"\n >\u26A0 {{ driftHint(field) }}</span\n >\n }\n @if (field.criticalField && !readonly) {\n <button\n type=\"button\"\n class=\"ngx-form__lock\"\n [attr.data-testid]=\"'lock-' + field.id\"\n [attr.aria-pressed]=\"!isFieldLocked(field)\"\n [attr.aria-label]=\"\n ui.text(isFieldLocked(field) ? 'unlockField' : 'lockField', language, {\n field: resolveFieldLabel(field),\n })\n \"\n [title]=\"ui.text(isFieldLocked(field) ? 'criticalFieldUnlockHint' : 'criticalFieldLockHint', language)\"\n (click)=\"toggleFieldLock(field)\"\n >\n {{ isFieldLocked(field) ? '\uD83D\uDD12' : '\uD83D\uDD13' }}\n </button>\n }\n </div>\n }\n </div>\n }\n </div>\n\n <!-- Submit button \u2014 gated by RBAC and isSaving lock -->\n @if (!readonly && permissions.canEdit) {\n <div class=\"ngx-form__actions\" data-testid=\"form-actions\">\n <button type=\"button\" class=\"ngx-form__reset\" data-testid=\"form-reset\" (click)=\"reset()\" [disabled]=\"isSaving()\">\n {{ ui.text('reset', language) }}\n </button>\n <!--\n Two different \"unavailable\" states, and they are not the same thing.\n\n `disabled` is for what a retry cannot fix \u2014 a save in flight, an async check still\n outstanding. `data-blocked` is for validation: the button stays fully operable,\n because pressing it is how the user gets the error summary, but it should not look\n ready either. The stylesheet mutes it without taking the click away.\n\n Deliberately not `aria-disabled`. That announces a control as present-but-inoperable,\n and this one is operable and useful \u2014 pressing it raises a `role=\"alert\"` naming\n every field at fault, which is more than a disabled button could ever say. Tooling\n agrees: Playwright refuses to click through `aria-disabled`, and it would be right\n to, because the attribute would be claiming something untrue.\n -->\n <button\n type=\"submit\"\n class=\"ngx-form__submit\"\n data-testid=\"form-submit\"\n [disabled]=\"submitDisabled\"\n [attr.data-blocked]=\"submitBlocked ? '' : null\"\n [title]=\"ui.text('saveShortcutHint', language)\"\n >\n @if (isSaving() || loading) {\n {{ ui.text('saving', language) }}\n } @else {\n {{ ui.text('save', language) }}\n }\n </button>\n </div>\n }\n </form>\n} @else {\n <!-- permissions.view denied: render nothing of the record.\n Presentational only \u2014 the server must not send data this user may not see. -->\n <div class=\"ngx-form__denied\" role=\"status\" data-testid=\"form-access-denied\">\n {{ ui.text('accessDenied', language) }}\n </div>\n}\n", styles: [".ngx-form__panel{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));gap:var(--ngx-gap, 18px);align-items:start}.ngx-form__field-slot{display:flex;align-items:flex-start;gap:6px;min-width:0}.ngx-form__field-slot ngx-dynamic-field{flex:1;min-width:0}.ngx-form__lock{flex:0 0 auto;margin-top:22px;background:none;border:1px solid transparent;border-radius:var(--ngx-radius-sm, 6px);cursor:pointer;font-size:14px;line-height:1;padding:4px 6px}.ngx-form__lock:hover{border-color:var(--ngx-color-border, #d1d5db);background:var(--ngx-color-surface-alt, #f9fafb)}@media(max-width:640px){.ngx-form__panel{grid-template-columns:minmax(0,1fr)}.ngx-form__field-slot{grid-column:1 / -1!important}}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: DynamicFieldComponent, selector: "ngx-dynamic-field", inputs: ["field", "control", "config", "currentTabId", "language", "readonly", "userRoles"] }, { kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
9414
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: DynamicFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
9415
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.25", type: DynamicFormComponent, isStandalone: true, selector: "ngx-dynamic-form", inputs: { config: "config", rules: "rules", initialData: "initialData", originalData: "originalData", userRoles: "userRoles", language: "language", readonly: "readonly", readOnlyFields: "readOnlyFields", showInfoBanners: "showInfoBanners", loading: "loading", error: "error", changeDebounceMs: "changeDebounceMs", preview: "preview", layout: "layout" }, outputs: { formSubmit: "formSubmit", formChange: "formChange", formReset: "formReset", activeTabChange: "activeTabChange", saveRejected: "saveRejected" }, host: { listeners: { "window:keydown": "handleKeyboardEvent($event)" } }, providers: [EntityRefSelectionService, FormStructureService], viewQueries: [{ propertyName: "formPanel", first: true, predicate: ["formPanel"], descendants: true }, { propertyName: "fieldHosts", predicate: DynamicFieldComponent, descendants: true }], usesOnChanges: true, ngImport: i0, template: "@if (canView) {\n <form [formGroup]=\"form\" (ngSubmit)=\"submit()\" class=\"ngx-form\" [class.ngx-form--readonly]=\"readonly\" novalidate>\n <!-- Error message banner -->\n @if (error) {\n <div class=\"ngx-form__error-banner\" role=\"alert\" data-testid=\"form-error\">{{ error }}</div>\n }\n\n <!--\n Why the last Save did nothing.\n Only after a refused submit \u2014 see `submitAttempted`. Each entry jumps to its field,\n switching tabs on the way, because the field is usually not the one on screen.\n -->\n @if (submitAttempted() && invalidFields().length > 0) {\n <div class=\"ngx-form__error-summary\" role=\"alert\" data-testid=\"error-summary\" tabindex=\"-1\">\n <!--\n `error-summary-heading`, not `-title`: the links below are `error-summary-<fieldId>`,\n and a field whose id happens to be `title` would then share a test id with this line.\n The `extensions` entity has exactly such a field.\n -->\n <p class=\"ngx-form__error-summary-title\" data-testid=\"error-summary-heading\">\n {{ ui.text('errorSummaryTitle', language, { count: invalidFields().length }) }}\n </p>\n <ul class=\"ngx-form__error-summary-list\">\n @for (entry of invalidFields(); track entry.field.id) {\n <li>\n <!--\n The field, and what is wrong with it \u2014 the same sentence the field renders\n under itself, so the summary is not a second opinion. Naming only the field\n left the user to visit each one to find out what it wanted.\n -->\n <button\n type=\"button\"\n class=\"ngx-form__error-summary-link\"\n [attr.data-testid]=\"'error-summary-' + entry.field.id\"\n (click)=\"jumpToField(entry.field.id)\"\n >\n <span class=\"ngx-form__error-summary-field\">{{ resolveFieldLabel(entry.field) }}</span>\n @if (entry.message) {\n <span class=\"ngx-form__error-summary-reason\">{{ entry.message }}</span>\n }\n </button>\n </li>\n }\n </ul>\n </div>\n }\n\n <!-- Rules Info Banners -->\n @for (infoKey of showInfoBanners ? Object.keys(ruleResult().infoBanners) : []; track infoKey) {\n <div class=\"ngx-form__info-banner\" role=\"status\" [attr.data-testid]=\"'info-banner-' + infoKey\">\n \u2139\uFE0F {{ ruleResult().infoBanners[infoKey] }}\n </div>\n }\n\n <!-- Rules Validation Errors & Warnings -->\n @for (errKey of Object.keys(ruleResult().validationErrors); track errKey) {\n <div class=\"ngx-form__error-banner\" role=\"alert\" [attr.data-testid]=\"'rule-error-' + errKey\">\n \u26A0\uFE0F {{ ruleResult().validationErrors[errKey] }}\n </div>\n }\n @for (warnKey of Object.keys(ruleResult().validationWarnings); track warnKey) {\n <div class=\"ngx-form__warning-banner\" role=\"status\" [attr.data-testid]=\"'rule-warning-' + warnKey\">\n \u26A1 {{ ruleResult().validationWarnings[warnKey] }}\n </div>\n }\n\n <!-- Deferred VALUE_CHANGED notice for criticalField edits (vs session baseline) -->\n @if (changedCriticalFields().length > 0) {\n <div class=\"ngx-form__warning-banner\" role=\"status\" data-testid=\"critical-change-banner\">\n {{ ui.text('criticalFieldChanged', language, { fields: changedCriticalFieldLabels() }) }}\n </div>\n }\n\n <!-- Primary Tab bar \u2014 only if visible tabs defined -->\n @if (visibleTabs.length > 0) {\n <div class=\"ngx-form__tabs\" role=\"tablist\" data-testid=\"tab-strip\">\n @for (tab of visibleTabs; track tab.id) {\n <button\n type=\"button\"\n class=\"ngx-form__tab\"\n [attr.data-testid]=\"'tab-' + tab.id\"\n [class.ngx-form__tab--active]=\"activeTab() === tab.id\"\n (click)=\"setActiveTab(tab.id)\"\n role=\"tab\"\n [attr.aria-selected]=\"activeTab() === tab.id\"\n >\n {{ resolveTabLabel(tab) }}\n <!--\n How many fields on this tab are stopping the save.\n The count is in the accessible name rather than the badge, because \"3\" read out\n after a tab name says nothing about what it counts.\n -->\n @if (tabErrorCount(tab); as errors) {\n <span\n class=\"ngx-form__tab-badge\"\n [attr.data-testid]=\"'tab-errors-' + tab.id\"\n [attr.aria-label]=\"ui.text('tabErrorCount', language, { count: errors })\"\n >{{ errors }}</span\n >\n }\n </button>\n }\n </div>\n }\n\n <!-- Sub-tab bar \u2014 only if active tab has sub-tabs -->\n @if (visibleSubTabs.length > 0) {\n <div\n class=\"ngx-form__tabs ngx-form__tabs--sub\"\n role=\"tablist\"\n data-testid=\"subtab-strip\"\n style=\"margin-top: -12px; margin-bottom: 12px; font-size: 13px\"\n >\n @for (subTab of visibleSubTabs; track subTab.id) {\n <button\n type=\"button\"\n class=\"ngx-form__tab ngx-form__tab--sub\"\n [attr.data-testid]=\"'subtab-' + subTab.id\"\n [class.ngx-form__tab--active]=\"activeSubTabConfig?.id === subTab.id\"\n (click)=\"setActiveSubTab(subTab.id)\"\n role=\"tab\"\n [attr.aria-selected]=\"activeSubTabConfig?.id === subTab.id\"\n >\n {{ resolveTabLabel(subTab) }}\n @if (subTabErrorCount(subTab); as errors) {\n <span\n class=\"ngx-form__tab-badge\"\n [attr.data-testid]=\"'subtab-errors-' + subTab.id\"\n [attr.aria-label]=\"ui.text('tabErrorCount', language, { count: errors })\"\n >{{ errors }}</span\n >\n }\n </button>\n }\n </div>\n }\n\n <!--\n One tabpanel per tab, wrapping both branches. Focus moves here on a tab switch, and a\n module tab is still a tab \u2014 putting the panel semantics on only the fields branch left\n a module tab with nowhere for focus to land.\n -->\n <div role=\"tabpanel\" tabindex=\"-1\" #formPanel class=\"ngx-form__tabpanel\" [attr.aria-label]=\"activeTabLabel\">\n @if (activeTabModuleComponent) {\n <div class=\"ngx-form__module-panel\" data-testid=\"module-panel\">\n <ng-container\n *ngComponentOutlet=\"activeTabModuleComponent; inputs: (activeSubTabConfig || activeTabConfig)?.moduleInputs\"\n ></ng-container>\n </div>\n } @else {\n <!-- Fields for active tab (Responsive 12-column grid) -->\n <div class=\"ngx-form__panel\" data-testid=\"form-panel\">\n @for (field of fieldsForActiveTab; track field.id) {\n <div\n class=\"ngx-form__field-slot\"\n [style.gridColumn]=\"getFieldSpan(field)\"\n [attr.id]=\"'field-container-' + field.id\"\n tabindex=\"-1\"\n >\n <ngx-dynamic-field\n [field]=\"field\"\n [control]=\"getControl(field.id, (activeSubTabConfig || activeTabConfig)?.id)!\"\n [config]=\"config\"\n [currentTabId]=\"(activeSubTabConfig || activeTabConfig)?.id\"\n [language]=\"language\"\n [readonly]=\"isFieldReadonly(field)\"\n [userRoles]=\"userRoles\"\n />\n @if (field.hasDrift) {\n <!--\n Drift was previously visible only in the builder, so a referenced field whose\n source had changed looked completely normal to the person filling the form in.\n role=\"status\" rather than \"alert\": it is information, not an error.\n -->\n <span\n class=\"ngx-field__drift\"\n role=\"status\"\n [attr.data-testid]=\"'field-' + field.id + '-drift'\"\n [title]=\"driftHint(field)\"\n >\u26A0 {{ driftHint(field) }}</span\n >\n }\n @if (field.criticalField && !readonly) {\n <button\n type=\"button\"\n class=\"ngx-form__lock\"\n [attr.data-testid]=\"'lock-' + field.id\"\n [attr.aria-pressed]=\"!isFieldLocked(field)\"\n [attr.aria-label]=\"\n ui.text(isFieldLocked(field) ? 'unlockField' : 'lockField', language, {\n field: resolveFieldLabel(field),\n })\n \"\n [title]=\"ui.text(isFieldLocked(field) ? 'criticalFieldUnlockHint' : 'criticalFieldLockHint', language)\"\n (click)=\"toggleFieldLock(field)\"\n >\n {{ isFieldLocked(field) ? '\uD83D\uDD12' : '\uD83D\uDD13' }}\n </button>\n }\n </div>\n }\n </div>\n }\n </div>\n\n <!-- Submit button \u2014 gated by RBAC and isSaving lock -->\n @if (!readonly && permissions.canEdit) {\n <div class=\"ngx-form__actions\" data-testid=\"form-actions\">\n <button type=\"button\" class=\"ngx-form__reset\" data-testid=\"form-reset\" (click)=\"reset()\" [disabled]=\"isSaving()\">\n {{ ui.text('reset', language) }}\n </button>\n <!--\n Two different \"unavailable\" states, and they are not the same thing.\n\n `disabled` is for what a retry cannot fix \u2014 a save in flight, an async check still\n outstanding. `data-blocked` is for validation: the button stays fully operable,\n because pressing it is how the user gets the error summary, but it should not look\n ready either. The stylesheet mutes it without taking the click away.\n\n Deliberately not `aria-disabled`. That announces a control as present-but-inoperable,\n and this one is operable and useful \u2014 pressing it raises a `role=\"alert\"` naming\n every field at fault, which is more than a disabled button could ever say. Tooling\n agrees: Playwright refuses to click through `aria-disabled`, and it would be right\n to, because the attribute would be claiming something untrue.\n -->\n <button\n type=\"submit\"\n class=\"ngx-form__submit\"\n data-testid=\"form-submit\"\n [disabled]=\"submitDisabled\"\n [attr.data-blocked]=\"submitBlocked ? '' : null\"\n [title]=\"ui.text('saveShortcutHint', language)\"\n >\n @if (isSaving() || loading) {\n {{ ui.text('saving', language) }}\n } @else {\n {{ ui.text('save', language) }}\n }\n </button>\n </div>\n }\n </form>\n} @else {\n <!-- permissions.view denied: render nothing of the record.\n Presentational only \u2014 the server must not send data this user may not see. -->\n <div class=\"ngx-form__denied\" role=\"status\" data-testid=\"form-access-denied\">\n {{ ui.text('accessDenied', language) }}\n </div>\n}\n", styles: [".ngx-form__panel{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));gap:var(--ngx-gap, 18px);align-items:start}.ngx-form__field-slot{display:flex;align-items:flex-start;gap:6px;min-width:0}.ngx-form__field-slot ngx-dynamic-field{flex:1;min-width:0}.ngx-form__lock{flex:0 0 auto;margin-top:22px;background:none;border:1px solid transparent;border-radius:var(--ngx-radius-sm, 6px);cursor:pointer;font-size:14px;line-height:1;padding:4px 6px}.ngx-form__lock:hover{border-color:var(--ngx-color-border, #d1d5db);background:var(--ngx-color-surface-alt, #f9fafb)}@media(max-width:640px){.ngx-form__panel{grid-template-columns:minmax(0,1fr)}.ngx-form__field-slot{grid-column:1 / -1!important}}\n"], dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: DynamicFieldComponent, selector: "ngx-dynamic-field", inputs: ["field", "control", "config", "currentTabId", "language", "readonly", "userRoles"] }, { kind: "directive", type: NgComponentOutlet, selector: "[ngComponentOutlet]", inputs: ["ngComponentOutlet", "ngComponentOutletInputs", "ngComponentOutletInjector", "ngComponentOutletEnvironmentInjector", "ngComponentOutletContent", "ngComponentOutletNgModule"], exportAs: ["ngComponentOutlet"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
9389
9416
|
}
|
|
9390
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
9417
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: DynamicFormComponent, decorators: [{
|
|
9391
9418
|
type: Component,
|
|
9392
9419
|
args: [{ changeDetection: ChangeDetectionStrategy.OnPush, selector: 'ngx-dynamic-form', standalone: true, imports: [ReactiveFormsModule, DynamicFieldComponent, NgComponentOutlet], providers: [EntityRefSelectionService, FormStructureService], template: "@if (canView) {\n <form [formGroup]=\"form\" (ngSubmit)=\"submit()\" class=\"ngx-form\" [class.ngx-form--readonly]=\"readonly\" novalidate>\n <!-- Error message banner -->\n @if (error) {\n <div class=\"ngx-form__error-banner\" role=\"alert\" data-testid=\"form-error\">{{ error }}</div>\n }\n\n <!--\n Why the last Save did nothing.\n Only after a refused submit \u2014 see `submitAttempted`. Each entry jumps to its field,\n switching tabs on the way, because the field is usually not the one on screen.\n -->\n @if (submitAttempted() && invalidFields().length > 0) {\n <div class=\"ngx-form__error-summary\" role=\"alert\" data-testid=\"error-summary\" tabindex=\"-1\">\n <!--\n `error-summary-heading`, not `-title`: the links below are `error-summary-<fieldId>`,\n and a field whose id happens to be `title` would then share a test id with this line.\n The `extensions` entity has exactly such a field.\n -->\n <p class=\"ngx-form__error-summary-title\" data-testid=\"error-summary-heading\">\n {{ ui.text('errorSummaryTitle', language, { count: invalidFields().length }) }}\n </p>\n <ul class=\"ngx-form__error-summary-list\">\n @for (entry of invalidFields(); track entry.field.id) {\n <li>\n <!--\n The field, and what is wrong with it \u2014 the same sentence the field renders\n under itself, so the summary is not a second opinion. Naming only the field\n left the user to visit each one to find out what it wanted.\n -->\n <button\n type=\"button\"\n class=\"ngx-form__error-summary-link\"\n [attr.data-testid]=\"'error-summary-' + entry.field.id\"\n (click)=\"jumpToField(entry.field.id)\"\n >\n <span class=\"ngx-form__error-summary-field\">{{ resolveFieldLabel(entry.field) }}</span>\n @if (entry.message) {\n <span class=\"ngx-form__error-summary-reason\">{{ entry.message }}</span>\n }\n </button>\n </li>\n }\n </ul>\n </div>\n }\n\n <!-- Rules Info Banners -->\n @for (infoKey of showInfoBanners ? Object.keys(ruleResult().infoBanners) : []; track infoKey) {\n <div class=\"ngx-form__info-banner\" role=\"status\" [attr.data-testid]=\"'info-banner-' + infoKey\">\n \u2139\uFE0F {{ ruleResult().infoBanners[infoKey] }}\n </div>\n }\n\n <!-- Rules Validation Errors & Warnings -->\n @for (errKey of Object.keys(ruleResult().validationErrors); track errKey) {\n <div class=\"ngx-form__error-banner\" role=\"alert\" [attr.data-testid]=\"'rule-error-' + errKey\">\n \u26A0\uFE0F {{ ruleResult().validationErrors[errKey] }}\n </div>\n }\n @for (warnKey of Object.keys(ruleResult().validationWarnings); track warnKey) {\n <div class=\"ngx-form__warning-banner\" role=\"status\" [attr.data-testid]=\"'rule-warning-' + warnKey\">\n \u26A1 {{ ruleResult().validationWarnings[warnKey] }}\n </div>\n }\n\n <!-- Deferred VALUE_CHANGED notice for criticalField edits (vs session baseline) -->\n @if (changedCriticalFields().length > 0) {\n <div class=\"ngx-form__warning-banner\" role=\"status\" data-testid=\"critical-change-banner\">\n {{ ui.text('criticalFieldChanged', language, { fields: changedCriticalFieldLabels() }) }}\n </div>\n }\n\n <!-- Primary Tab bar \u2014 only if visible tabs defined -->\n @if (visibleTabs.length > 0) {\n <div class=\"ngx-form__tabs\" role=\"tablist\" data-testid=\"tab-strip\">\n @for (tab of visibleTabs; track tab.id) {\n <button\n type=\"button\"\n class=\"ngx-form__tab\"\n [attr.data-testid]=\"'tab-' + tab.id\"\n [class.ngx-form__tab--active]=\"activeTab() === tab.id\"\n (click)=\"setActiveTab(tab.id)\"\n role=\"tab\"\n [attr.aria-selected]=\"activeTab() === tab.id\"\n >\n {{ resolveTabLabel(tab) }}\n <!--\n How many fields on this tab are stopping the save.\n The count is in the accessible name rather than the badge, because \"3\" read out\n after a tab name says nothing about what it counts.\n -->\n @if (tabErrorCount(tab); as errors) {\n <span\n class=\"ngx-form__tab-badge\"\n [attr.data-testid]=\"'tab-errors-' + tab.id\"\n [attr.aria-label]=\"ui.text('tabErrorCount', language, { count: errors })\"\n >{{ errors }}</span\n >\n }\n </button>\n }\n </div>\n }\n\n <!-- Sub-tab bar \u2014 only if active tab has sub-tabs -->\n @if (visibleSubTabs.length > 0) {\n <div\n class=\"ngx-form__tabs ngx-form__tabs--sub\"\n role=\"tablist\"\n data-testid=\"subtab-strip\"\n style=\"margin-top: -12px; margin-bottom: 12px; font-size: 13px\"\n >\n @for (subTab of visibleSubTabs; track subTab.id) {\n <button\n type=\"button\"\n class=\"ngx-form__tab ngx-form__tab--sub\"\n [attr.data-testid]=\"'subtab-' + subTab.id\"\n [class.ngx-form__tab--active]=\"activeSubTabConfig?.id === subTab.id\"\n (click)=\"setActiveSubTab(subTab.id)\"\n role=\"tab\"\n [attr.aria-selected]=\"activeSubTabConfig?.id === subTab.id\"\n >\n {{ resolveTabLabel(subTab) }}\n @if (subTabErrorCount(subTab); as errors) {\n <span\n class=\"ngx-form__tab-badge\"\n [attr.data-testid]=\"'subtab-errors-' + subTab.id\"\n [attr.aria-label]=\"ui.text('tabErrorCount', language, { count: errors })\"\n >{{ errors }}</span\n >\n }\n </button>\n }\n </div>\n }\n\n <!--\n One tabpanel per tab, wrapping both branches. Focus moves here on a tab switch, and a\n module tab is still a tab \u2014 putting the panel semantics on only the fields branch left\n a module tab with nowhere for focus to land.\n -->\n <div role=\"tabpanel\" tabindex=\"-1\" #formPanel class=\"ngx-form__tabpanel\" [attr.aria-label]=\"activeTabLabel\">\n @if (activeTabModuleComponent) {\n <div class=\"ngx-form__module-panel\" data-testid=\"module-panel\">\n <ng-container\n *ngComponentOutlet=\"activeTabModuleComponent; inputs: (activeSubTabConfig || activeTabConfig)?.moduleInputs\"\n ></ng-container>\n </div>\n } @else {\n <!-- Fields for active tab (Responsive 12-column grid) -->\n <div class=\"ngx-form__panel\" data-testid=\"form-panel\">\n @for (field of fieldsForActiveTab; track field.id) {\n <div\n class=\"ngx-form__field-slot\"\n [style.gridColumn]=\"getFieldSpan(field)\"\n [attr.id]=\"'field-container-' + field.id\"\n tabindex=\"-1\"\n >\n <ngx-dynamic-field\n [field]=\"field\"\n [control]=\"getControl(field.id, (activeSubTabConfig || activeTabConfig)?.id)!\"\n [config]=\"config\"\n [currentTabId]=\"(activeSubTabConfig || activeTabConfig)?.id\"\n [language]=\"language\"\n [readonly]=\"isFieldReadonly(field)\"\n [userRoles]=\"userRoles\"\n />\n @if (field.hasDrift) {\n <!--\n Drift was previously visible only in the builder, so a referenced field whose\n source had changed looked completely normal to the person filling the form in.\n role=\"status\" rather than \"alert\": it is information, not an error.\n -->\n <span\n class=\"ngx-field__drift\"\n role=\"status\"\n [attr.data-testid]=\"'field-' + field.id + '-drift'\"\n [title]=\"driftHint(field)\"\n >\u26A0 {{ driftHint(field) }}</span\n >\n }\n @if (field.criticalField && !readonly) {\n <button\n type=\"button\"\n class=\"ngx-form__lock\"\n [attr.data-testid]=\"'lock-' + field.id\"\n [attr.aria-pressed]=\"!isFieldLocked(field)\"\n [attr.aria-label]=\"\n ui.text(isFieldLocked(field) ? 'unlockField' : 'lockField', language, {\n field: resolveFieldLabel(field),\n })\n \"\n [title]=\"ui.text(isFieldLocked(field) ? 'criticalFieldUnlockHint' : 'criticalFieldLockHint', language)\"\n (click)=\"toggleFieldLock(field)\"\n >\n {{ isFieldLocked(field) ? '\uD83D\uDD12' : '\uD83D\uDD13' }}\n </button>\n }\n </div>\n }\n </div>\n }\n </div>\n\n <!-- Submit button \u2014 gated by RBAC and isSaving lock -->\n @if (!readonly && permissions.canEdit) {\n <div class=\"ngx-form__actions\" data-testid=\"form-actions\">\n <button type=\"button\" class=\"ngx-form__reset\" data-testid=\"form-reset\" (click)=\"reset()\" [disabled]=\"isSaving()\">\n {{ ui.text('reset', language) }}\n </button>\n <!--\n Two different \"unavailable\" states, and they are not the same thing.\n\n `disabled` is for what a retry cannot fix \u2014 a save in flight, an async check still\n outstanding. `data-blocked` is for validation: the button stays fully operable,\n because pressing it is how the user gets the error summary, but it should not look\n ready either. The stylesheet mutes it without taking the click away.\n\n Deliberately not `aria-disabled`. That announces a control as present-but-inoperable,\n and this one is operable and useful \u2014 pressing it raises a `role=\"alert\"` naming\n every field at fault, which is more than a disabled button could ever say. Tooling\n agrees: Playwright refuses to click through `aria-disabled`, and it would be right\n to, because the attribute would be claiming something untrue.\n -->\n <button\n type=\"submit\"\n class=\"ngx-form__submit\"\n data-testid=\"form-submit\"\n [disabled]=\"submitDisabled\"\n [attr.data-blocked]=\"submitBlocked ? '' : null\"\n [title]=\"ui.text('saveShortcutHint', language)\"\n >\n @if (isSaving() || loading) {\n {{ ui.text('saving', language) }}\n } @else {\n {{ ui.text('save', language) }}\n }\n </button>\n </div>\n }\n </form>\n} @else {\n <!-- permissions.view denied: render nothing of the record.\n Presentational only \u2014 the server must not send data this user may not see. -->\n <div class=\"ngx-form__denied\" role=\"status\" data-testid=\"form-access-denied\">\n {{ ui.text('accessDenied', language) }}\n </div>\n}\n", styles: [".ngx-form__panel{display:grid;grid-template-columns:repeat(12,minmax(0,1fr));gap:var(--ngx-gap, 18px);align-items:start}.ngx-form__field-slot{display:flex;align-items:flex-start;gap:6px;min-width:0}.ngx-form__field-slot ngx-dynamic-field{flex:1;min-width:0}.ngx-form__lock{flex:0 0 auto;margin-top:22px;background:none;border:1px solid transparent;border-radius:var(--ngx-radius-sm, 6px);cursor:pointer;font-size:14px;line-height:1;padding:4px 6px}.ngx-form__lock:hover{border-color:var(--ngx-color-border, #d1d5db);background:var(--ngx-color-surface-alt, #f9fafb)}@media(max-width:640px){.ngx-form__panel{grid-template-columns:minmax(0,1fr)}.ngx-form__field-slot{grid-column:1 / -1!important}}\n"] }]
|
|
9393
9420
|
}], propDecorators: { config: [{
|
|
@@ -10000,10 +10027,10 @@ class DynamicRecordFormComponent {
|
|
|
10000
10027
|
jumpToField(fieldId) {
|
|
10001
10028
|
this.dynamicFormComp?.jumpToField(fieldId);
|
|
10002
10029
|
}
|
|
10003
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
10004
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.24", type: DynamicRecordFormComponent, isStandalone: true, selector: "ngx-dynamic-record-form", inputs: { config: "config", rules: "rules", initialData: "initialData", userRoles: "userRoles", language: "language", readonly: "readonly", loading: "loading", error: "error", isReadOnly: "isReadOnly", readOnlyFields: "readOnlyFields", layout: "layout", viewMode: "viewMode" }, outputs: { formSubmit: "formSubmit", formChange: "formChange", formReset: "formReset", sectionSave: "sectionSave", saveRejected: "saveRejected" }, viewQueries: [{ propertyName: "dynamicFormComp", first: true, predicate: DynamicFormComponent, descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"ngx-record-editor\">\n <!-- Record Header -->\n <div class=\"ngx-record-editor__header\">\n <div class=\"ngx-record-editor__title-group\">\n @if (profileImageUrl) {\n <img\n class=\"ngx-record-editor__avatar\"\n data-testid=\"header-profile-image\"\n [src]=\"profileImageUrl\"\n [alt]=\"recordHeading\"\n />\n } @else {\n <div class=\"ngx-record-editor__avatar\" data-testid=\"header-avatar-initial\">\n {{ avatarLetter }}\n </div>\n }\n <div>\n <!--\n The record's own name, with the entity underneath. These were the other way round \u2014\n both showing the entity \u2014 so the header never said which record was open.\n -->\n <h2 class=\"ngx-record-editor__title\" data-testid=\"record-title\">\n {{ recordHeading }}\n </h2>\n <span class=\"ngx-record-editor__subtitle\">{{ ui.text('entityLabel', language, { entity: recordTitle }) }}</span>\n </div>\n </div>\n\n <!-- Record-level status switch, outside section editing -->\n @if (headerToggleField; as toggleField) {\n <label class=\"ngx-record-editor__header-toggle\">\n <span>{{ formatFieldLabel(toggleField) }}</span>\n <input\n type=\"checkbox\"\n role=\"switch\"\n data-testid=\"header-toggle\"\n [checked]=\"headerToggleValue\"\n [disabled]=\"recordReadOnly\"\n [attr.aria-checked]=\"headerToggleValue\"\n (change)=\"toggleHeaderStatus()\"\n />\n <span class=\"ngx-record-editor__header-toggle-text\">{{ ui.text(headerToggleValue ? 'yes' : 'no', language) }}</span>\n </label>\n }\n </div>\n\n <!-- Dismissible info banners (rules `info` action) -->\n @for (banner of infoBanners; track banner.fieldId) {\n <div\n class=\"ngx-record-editor__banner ngx-record-editor__banner--info\"\n role=\"status\"\n [attr.data-testid]=\"'info-banner-' + banner.fieldId\"\n >\n <span>\u2139\uFE0F {{ banner.message }}</span>\n <button\n type=\"button\"\n class=\"ngx-record-editor__banner-dismiss\"\n [attr.data-testid]=\"'dismiss-' + banner.fieldId\"\n [attr.aria-label]=\"ui.text('dismissBanner', language, { message: banner.message })\"\n (click)=\"dismissInfoBanner(banner.fieldId)\"\n >\n \u2715\n </button>\n </div>\n }\n\n <!-- Modification baseline banner -->\n @if (isModified()) {\n <div class=\"ngx-record-editor__banner ngx-record-editor__banner--warning\">\n {{ ui.text('recordModified', language) }}\n </div>\n }\n\n <!-- Summary Panel (showOnMinimize fields with interactive jump) -->\n @if (summaryFields().length > 0) {\n <div class=\"ngx-record-editor__summary-panel\" data-testid=\"summary-panel\">\n @for (sf of summaryFields(); track sf.id) {\n <div\n class=\"ngx-record-editor__summary-item\"\n [attr.data-testid]=\"'summary-' + sf.id\"\n (click)=\"jumpToField(sf.id)\"\n [title]=\"ui.text('jumpToField', language, { field: formatFieldLabel(sf) })\"\n >\n <span class=\"ngx-record-editor__summary-label\">{{ formatFieldLabel(sf) }}</span>\n <span class=\"ngx-record-editor__summary-val\">{{ formatFieldValue(sf) }}</span>\n </div>\n }\n </div>\n }\n\n <!-- Section edit bar: one tab is edited at a time, and validated on its own -->\n @if (viewMode && !recordReadOnly) {\n <div class=\"ngx-record-editor__section-bar\">\n @if (isEditingActiveTab) {\n <span class=\"ngx-record-editor__section-hint\">{{ ui.text('editingSection', language) }}</span>\n <button type=\"button\" class=\"ngx-form__reset\" data-testid=\"cancel-section\" (click)=\"cancelSection()\">\n {{ ui.text('cancel', language) }}\n </button>\n <button type=\"button\" class=\"ngx-form__submit\" data-testid=\"save-section\" (click)=\"saveSection()\">\n {{ ui.text('saveSection', language) }}\n </button>\n } @else {\n <button type=\"button\" class=\"ngx-form__reset\" data-testid=\"edit-section\" (click)=\"editSection()\">\n {{ ui.text('editSection', language) }}\n </button>\n }\n </div>\n }\n\n @if (Object.keys(sectionErrors()).length > 0) {\n <div class=\"ngx-record-editor__banner ngx-record-editor__banner--error\" role=\"alert\" data-testid=\"section-errors\">\n @for (key of Object.keys(sectionErrors()); track key) {\n <div>\u26A0\uFE0F {{ sectionErrors()[key] }}</div>\n }\n </div>\n }\n\n <!-- Array rows: listed here, edited in the drawer below -->\n @for (arrayField of arrayFieldsForActiveTab; track arrayField.id) {\n <div class=\"ngx-record-editor__rows\" [attr.data-testid]=\"'rows-' + arrayField.id\">\n <div class=\"ngx-record-editor__rows-head\">\n <span class=\"ngx-record-editor__summary-label\">{{ formatFieldLabel(arrayField) }}</span>\n @if (!sectionReadOnly) {\n <button\n type=\"button\"\n class=\"ngx-form__reset\"\n [attr.data-testid]=\"'add-row-' + arrayField.id\"\n (click)=\"openAddRow(arrayField)\"\n >\n {{ ui.text('addRow', language) }}\n </button>\n }\n </div>\n\n @for (row of rowsOf(arrayField); track $index) {\n <div class=\"ngx-record-editor__row\" [attr.data-testid]=\"'row-' + arrayField.id + '-' + $index\">\n <span class=\"ngx-record-editor__row-text\">{{ rowSummary(arrayField, row) }}</span>\n @if (!sectionReadOnly) {\n <button\n type=\"button\"\n class=\"ngx-record-editor__row-btn\"\n [attr.data-testid]=\"'edit-row-' + arrayField.id + '-' + $index\"\n (click)=\"openEditRow(arrayField, $index)\"\n >\n {{ ui.text('edit', language) }}\n </button>\n <button\n type=\"button\"\n class=\"ngx-record-editor__row-btn\"\n [attr.data-testid]=\"'delete-row-' + arrayField.id + '-' + $index\"\n (click)=\"deleteRow(arrayField, $index)\"\n >\n {{ ui.text('delete', language) }}\n </button>\n }\n </div>\n } @empty {\n <p class=\"ngx-record-editor__row-empty\">{{ ui.text('noRows', language) }}</p>\n }\n </div>\n }\n\n <!--\n Row drawer. Deliberately outside the tab panel: building a row's FormGroup inside the\n tab view races the tab's own initialisation (the reference hit this with FormArray).\n -->\n @if (inlineRowField(); as rowField) {\n <div class=\"ngx-record-editor__drawer\" data-testid=\"row-drawer\" [formGroup]=\"inlineRowForm!\">\n <div class=\"ngx-record-editor__rows-head\">\n <strong>{{ rowDrawerTitle(rowField) }}</strong>\n </div>\n\n <div class=\"ngx-form__panel\">\n @for (child of rowField.children ?? []; track child.id) {\n <ngx-dynamic-field\n [field]=\"child\"\n [control]=\"inlineRowForm!.get(child.id)!\"\n [config]=\"config\"\n [language]=\"language\"\n [readonly]=\"false\"\n [userRoles]=\"userRoles\"\n [style.gridColumn]=\"'span 12'\"\n />\n }\n </div>\n\n <div class=\"ngx-record-editor__section-bar\">\n <button type=\"button\" class=\"ngx-form__reset\" data-testid=\"cancel-row\" (click)=\"cancelRow()\">\n {{ ui.text('cancel', language) }}\n </button>\n <button type=\"button\" class=\"ngx-form__submit\" data-testid=\"save-row\" (click)=\"saveRow()\">\n {{ ui.text('saveRow', language) }}\n </button>\n </div>\n </div>\n }\n\n <!-- Main Dynamic Form -->\n <ngx-dynamic-form\n [config]=\"config\"\n [rules]=\"rules\"\n [initialData]=\"initialData\"\n [originalData]=\"originalBaseline()\"\n [userRoles]=\"userRoles\"\n [language]=\"language\"\n [readonly]=\"sectionReadOnly\"\n [readOnlyFields]=\"readOnlyFields\"\n [layout]=\"layout\"\n [showInfoBanners]=\"false\"\n [loading]=\"loading\"\n [error]=\"error\"\n (formChange)=\"onFormChange($event)\"\n (formSubmit)=\"onFormSubmit($event)\"\n (saveRejected)=\"saveRejected.emit($event)\"\n (formReset)=\"onFormReset()\"\n (activeTabChange)=\"onActiveTabChange($event)\"\n />\n</div>\n", styles: [".ngx-record-editor{display:flex;flex-direction:column;gap:var(--ngx-gap, 16px);background:var(--ngx-color-surface, #ffffff);border:1px solid var(--ngx-color-border, #e5e7eb);border-radius:var(--ngx-radius, 12px);padding:20px}.ngx-record-editor__header{display:flex;align-items:center;justify-content:space-between;gap:14px;padding-bottom:var(--ngx-gap-sm, 16px);border-bottom:1px solid var(--ngx-color-border, #f3f4f6)}.ngx-record-editor__title-group{display:flex;align-items:center;gap:12px;min-width:0}.ngx-record-editor__avatar{width:44px;height:44px;border-radius:50%;object-fit:cover;background:var(--ngx-color-accent-soft, #e0e7ff);color:var(--ngx-color-accent, #4f46e5);border:1px solid var(--ngx-color-border, transparent);display:flex;align-items:center;justify-content:center;font-weight:700;font-size:17px;flex:none}.ngx-record-editor__title{font-size:19px;font-weight:700;color:var(--ngx-color-text, #111827);margin:0;letter-spacing:-.01em}.ngx-record-editor__subtitle{font-size:var(--ngx-hint-size, 13px);color:var(--ngx-color-muted, #6b7280)}.ngx-record-editor__header-toggle{display:flex;align-items:center;gap:8px;font-size:var(--ngx-label-size, 13px);font-weight:var(--ngx-label-weight, 600);color:var(--ngx-color-text, #374151)}.ngx-record-editor__header-toggle-text{font-weight:600}.ngx-record-editor__banner{padding:11px 14px;border-radius:var(--ngx-radius-sm, 8px);font-size:var(--ngx-font-size, 13px);border:1px solid transparent;border-left-width:3px}.ngx-record-editor__banner--info{background:var(--ngx-color-info-soft, #eff6ff);border-color:var(--ngx-color-border, #dbeafe);border-left-color:var(--ngx-color-info, #1d4ed8);color:var(--ngx-color-info, #1d4ed8);display:flex;align-items:center;justify-content:space-between;gap:12px}.ngx-record-editor__banner--error{background:var(--ngx-color-error-soft, #fef2f2);border-color:var(--ngx-color-border, #fecaca);border-left-color:var(--ngx-color-error, #b91c1c);color:var(--ngx-color-error, #b91c1c)}.ngx-record-editor__banner--warning{background:var(--ngx-color-warning-soft, #fffbeb);border-color:var(--ngx-color-border, #fef3c7);border-left-color:var(--ngx-color-warning, #b45309);color:var(--ngx-color-warning, #b45309)}.ngx-record-editor__banner-dismiss{background:none;border:none;cursor:pointer;color:inherit;font-size:14px;line-height:1;padding:2px 6px;border-radius:4px}.ngx-record-editor__banner-dismiss:hover{background:#0000000f}.ngx-record-editor__section-bar{display:flex;align-items:center;justify-content:flex-end;flex-wrap:wrap;gap:8px}.ngx-record-editor__section-hint{margin-right:auto;font-size:var(--ngx-hint-size, 12px);color:var(--ngx-color-muted, #6b7280)}.ngx-record-editor__rows{border:1px solid var(--ngx-color-border, #f3f4f6);border-radius:var(--ngx-radius-sm, 8px);padding:12px 14px;display:flex;flex-direction:column;gap:6px}.ngx-record-editor__rows-head{display:flex;align-items:center;justify-content:space-between;gap:8px}.ngx-record-editor__row{display:flex;align-items:center;gap:8px;padding:8px 10px;border-radius:var(--ngx-radius-sm, 6px);background:var(--ngx-color-surface-alt, #f9fafb)}.ngx-record-editor__row-text{flex:1;font-size:var(--ngx-font-size, 13px);overflow-wrap:anywhere}.ngx-record-editor__row-btn{background:var(--ngx-color-surface, none);border:1px solid var(--ngx-color-border, #e5e7eb);border-radius:var(--ngx-radius-sm, 6px);cursor:pointer;font:inherit;font-size:var(--ngx-hint-size, 12px);font-weight:var(--ngx-label-weight, 600);color:inherit;padding:4px 10px}.ngx-record-editor__row-btn:hover{border-color:var(--ngx-color-accent, #e5e7eb);color:var(--ngx-color-accent, inherit)}.ngx-record-editor__row-empty{font-size:var(--ngx-hint-size, 12px);color:var(--ngx-color-muted, #6b7280);margin:0}.ngx-record-editor__drawer{border:1px solid var(--ngx-color-border, #dbeafe);background:var(--ngx-color-surface-alt, #f8fafc);border-radius:var(--ngx-radius, 8px);padding:16px;display:flex;flex-direction:column;gap:12px}.ngx-record-editor__summary-panel{background:var(--ngx-color-surface-alt, #f9fafb);border:1px solid var(--ngx-color-border, #f3f4f6);border-radius:var(--ngx-radius, 8px);padding:12px 16px;display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:4px var(--ngx-gap, 12px)}.ngx-record-editor__summary-item{display:flex;flex-direction:column;cursor:pointer;padding:6px 10px;border-radius:var(--ngx-radius-sm, 6px);min-width:0;transition:background .15s}.ngx-record-editor__summary-item:hover{background:var(--ngx-color-accent-soft, #eff6ff)}.ngx-record-editor__summary-label{font-size:11px;text-transform:uppercase;letter-spacing:.05em;color:var(--ngx-color-muted, #6b7280);font-weight:600}.ngx-record-editor__summary-val{font-size:var(--ngx-font-size, 14px);font-weight:600;color:var(--ngx-color-text, #1f2937);overflow-wrap:anywhere}@media(prefers-reduced-motion:reduce){.ngx-record-editor__summary-item{transition:none}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: DynamicFormComponent, selector: "ngx-dynamic-form", inputs: ["config", "rules", "initialData", "originalData", "userRoles", "language", "readonly", "readOnlyFields", "showInfoBanners", "loading", "error", "changeDebounceMs", "preview", "layout"], outputs: ["formSubmit", "formChange", "formReset", "activeTabChange", "saveRejected"] }, { kind: "component", type: DynamicFieldComponent, selector: "ngx-dynamic-field", inputs: ["field", "control", "config", "currentTabId", "language", "readonly", "userRoles"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
10030
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: DynamicRecordFormComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
10031
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.25", type: DynamicRecordFormComponent, isStandalone: true, selector: "ngx-dynamic-record-form", inputs: { config: "config", rules: "rules", initialData: "initialData", userRoles: "userRoles", language: "language", readonly: "readonly", loading: "loading", error: "error", isReadOnly: "isReadOnly", readOnlyFields: "readOnlyFields", layout: "layout", viewMode: "viewMode" }, outputs: { formSubmit: "formSubmit", formChange: "formChange", formReset: "formReset", sectionSave: "sectionSave", saveRejected: "saveRejected" }, viewQueries: [{ propertyName: "dynamicFormComp", first: true, predicate: DynamicFormComponent, descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"ngx-record-editor\">\n <!-- Record Header -->\n <div class=\"ngx-record-editor__header\">\n <div class=\"ngx-record-editor__title-group\">\n @if (profileImageUrl) {\n <img\n class=\"ngx-record-editor__avatar\"\n data-testid=\"header-profile-image\"\n [src]=\"profileImageUrl\"\n [alt]=\"recordHeading\"\n />\n } @else {\n <div class=\"ngx-record-editor__avatar\" data-testid=\"header-avatar-initial\">\n {{ avatarLetter }}\n </div>\n }\n <div>\n <!--\n The record's own name, with the entity underneath. These were the other way round \u2014\n both showing the entity \u2014 so the header never said which record was open.\n -->\n <h2 class=\"ngx-record-editor__title\" data-testid=\"record-title\">\n {{ recordHeading }}\n </h2>\n <span class=\"ngx-record-editor__subtitle\">{{ ui.text('entityLabel', language, { entity: recordTitle }) }}</span>\n </div>\n </div>\n\n <!-- Record-level status switch, outside section editing -->\n @if (headerToggleField; as toggleField) {\n <label class=\"ngx-record-editor__header-toggle\">\n <span>{{ formatFieldLabel(toggleField) }}</span>\n <input\n type=\"checkbox\"\n role=\"switch\"\n data-testid=\"header-toggle\"\n [checked]=\"headerToggleValue\"\n [disabled]=\"recordReadOnly\"\n [attr.aria-checked]=\"headerToggleValue\"\n (change)=\"toggleHeaderStatus()\"\n />\n <span class=\"ngx-record-editor__header-toggle-text\">{{ ui.text(headerToggleValue ? 'yes' : 'no', language) }}</span>\n </label>\n }\n </div>\n\n <!-- Dismissible info banners (rules `info` action) -->\n @for (banner of infoBanners; track banner.fieldId) {\n <div\n class=\"ngx-record-editor__banner ngx-record-editor__banner--info\"\n role=\"status\"\n [attr.data-testid]=\"'info-banner-' + banner.fieldId\"\n >\n <span>\u2139\uFE0F {{ banner.message }}</span>\n <button\n type=\"button\"\n class=\"ngx-record-editor__banner-dismiss\"\n [attr.data-testid]=\"'dismiss-' + banner.fieldId\"\n [attr.aria-label]=\"ui.text('dismissBanner', language, { message: banner.message })\"\n (click)=\"dismissInfoBanner(banner.fieldId)\"\n >\n \u2715\n </button>\n </div>\n }\n\n <!-- Modification baseline banner -->\n @if (isModified()) {\n <div class=\"ngx-record-editor__banner ngx-record-editor__banner--warning\">\n {{ ui.text('recordModified', language) }}\n </div>\n }\n\n <!-- Summary Panel (showOnMinimize fields with interactive jump) -->\n @if (summaryFields().length > 0) {\n <div class=\"ngx-record-editor__summary-panel\" data-testid=\"summary-panel\">\n @for (sf of summaryFields(); track sf.id) {\n <div\n class=\"ngx-record-editor__summary-item\"\n [attr.data-testid]=\"'summary-' + sf.id\"\n (click)=\"jumpToField(sf.id)\"\n [title]=\"ui.text('jumpToField', language, { field: formatFieldLabel(sf) })\"\n >\n <span class=\"ngx-record-editor__summary-label\">{{ formatFieldLabel(sf) }}</span>\n <span class=\"ngx-record-editor__summary-val\">{{ formatFieldValue(sf) }}</span>\n </div>\n }\n </div>\n }\n\n <!-- Section edit bar: one tab is edited at a time, and validated on its own -->\n @if (viewMode && !recordReadOnly) {\n <div class=\"ngx-record-editor__section-bar\">\n @if (isEditingActiveTab) {\n <span class=\"ngx-record-editor__section-hint\">{{ ui.text('editingSection', language) }}</span>\n <button type=\"button\" class=\"ngx-form__reset\" data-testid=\"cancel-section\" (click)=\"cancelSection()\">\n {{ ui.text('cancel', language) }}\n </button>\n <button type=\"button\" class=\"ngx-form__submit\" data-testid=\"save-section\" (click)=\"saveSection()\">\n {{ ui.text('saveSection', language) }}\n </button>\n } @else {\n <button type=\"button\" class=\"ngx-form__reset\" data-testid=\"edit-section\" (click)=\"editSection()\">\n {{ ui.text('editSection', language) }}\n </button>\n }\n </div>\n }\n\n @if (Object.keys(sectionErrors()).length > 0) {\n <div class=\"ngx-record-editor__banner ngx-record-editor__banner--error\" role=\"alert\" data-testid=\"section-errors\">\n @for (key of Object.keys(sectionErrors()); track key) {\n <div>\u26A0\uFE0F {{ sectionErrors()[key] }}</div>\n }\n </div>\n }\n\n <!-- Array rows: listed here, edited in the drawer below -->\n @for (arrayField of arrayFieldsForActiveTab; track arrayField.id) {\n <div class=\"ngx-record-editor__rows\" [attr.data-testid]=\"'rows-' + arrayField.id\">\n <div class=\"ngx-record-editor__rows-head\">\n <span class=\"ngx-record-editor__summary-label\">{{ formatFieldLabel(arrayField) }}</span>\n @if (!sectionReadOnly) {\n <button\n type=\"button\"\n class=\"ngx-form__reset\"\n [attr.data-testid]=\"'add-row-' + arrayField.id\"\n (click)=\"openAddRow(arrayField)\"\n >\n {{ ui.text('addRow', language) }}\n </button>\n }\n </div>\n\n @for (row of rowsOf(arrayField); track $index) {\n <div class=\"ngx-record-editor__row\" [attr.data-testid]=\"'row-' + arrayField.id + '-' + $index\">\n <span class=\"ngx-record-editor__row-text\">{{ rowSummary(arrayField, row) }}</span>\n @if (!sectionReadOnly) {\n <button\n type=\"button\"\n class=\"ngx-record-editor__row-btn\"\n [attr.data-testid]=\"'edit-row-' + arrayField.id + '-' + $index\"\n (click)=\"openEditRow(arrayField, $index)\"\n >\n {{ ui.text('edit', language) }}\n </button>\n <button\n type=\"button\"\n class=\"ngx-record-editor__row-btn\"\n [attr.data-testid]=\"'delete-row-' + arrayField.id + '-' + $index\"\n (click)=\"deleteRow(arrayField, $index)\"\n >\n {{ ui.text('delete', language) }}\n </button>\n }\n </div>\n } @empty {\n <p class=\"ngx-record-editor__row-empty\">{{ ui.text('noRows', language) }}</p>\n }\n </div>\n }\n\n <!--\n Row drawer. Deliberately outside the tab panel: building a row's FormGroup inside the\n tab view races the tab's own initialisation (the reference hit this with FormArray).\n -->\n @if (inlineRowField(); as rowField) {\n <div class=\"ngx-record-editor__drawer\" data-testid=\"row-drawer\" [formGroup]=\"inlineRowForm!\">\n <div class=\"ngx-record-editor__rows-head\">\n <strong>{{ rowDrawerTitle(rowField) }}</strong>\n </div>\n\n <div class=\"ngx-form__panel\">\n @for (child of rowField.children ?? []; track child.id) {\n <ngx-dynamic-field\n [field]=\"child\"\n [control]=\"inlineRowForm!.get(child.id)!\"\n [config]=\"config\"\n [language]=\"language\"\n [readonly]=\"false\"\n [userRoles]=\"userRoles\"\n [style.gridColumn]=\"'span 12'\"\n />\n }\n </div>\n\n <div class=\"ngx-record-editor__section-bar\">\n <button type=\"button\" class=\"ngx-form__reset\" data-testid=\"cancel-row\" (click)=\"cancelRow()\">\n {{ ui.text('cancel', language) }}\n </button>\n <button type=\"button\" class=\"ngx-form__submit\" data-testid=\"save-row\" (click)=\"saveRow()\">\n {{ ui.text('saveRow', language) }}\n </button>\n </div>\n </div>\n }\n\n <!-- Main Dynamic Form -->\n <ngx-dynamic-form\n [config]=\"config\"\n [rules]=\"rules\"\n [initialData]=\"initialData\"\n [originalData]=\"originalBaseline()\"\n [userRoles]=\"userRoles\"\n [language]=\"language\"\n [readonly]=\"sectionReadOnly\"\n [readOnlyFields]=\"readOnlyFields\"\n [layout]=\"layout\"\n [showInfoBanners]=\"false\"\n [loading]=\"loading\"\n [error]=\"error\"\n (formChange)=\"onFormChange($event)\"\n (formSubmit)=\"onFormSubmit($event)\"\n (saveRejected)=\"saveRejected.emit($event)\"\n (formReset)=\"onFormReset()\"\n (activeTabChange)=\"onActiveTabChange($event)\"\n />\n</div>\n", styles: [".ngx-record-editor{display:flex;flex-direction:column;gap:var(--ngx-gap, 16px);background:var(--ngx-color-surface, #ffffff);border:1px solid var(--ngx-color-border, #e5e7eb);border-radius:var(--ngx-radius, 12px);padding:20px}.ngx-record-editor__header{display:flex;align-items:center;justify-content:space-between;gap:14px;padding-bottom:var(--ngx-gap-sm, 16px);border-bottom:1px solid var(--ngx-color-border, #f3f4f6)}.ngx-record-editor__title-group{display:flex;align-items:center;gap:12px;min-width:0}.ngx-record-editor__avatar{width:44px;height:44px;border-radius:50%;object-fit:cover;background:var(--ngx-color-accent-soft, #e0e7ff);color:var(--ngx-color-accent, #4f46e5);border:1px solid var(--ngx-color-border, transparent);display:flex;align-items:center;justify-content:center;font-weight:700;font-size:17px;flex:none}.ngx-record-editor__title{font-size:19px;font-weight:700;color:var(--ngx-color-text, #111827);margin:0;letter-spacing:-.01em}.ngx-record-editor__subtitle{font-size:var(--ngx-hint-size, 13px);color:var(--ngx-color-muted, #6b7280)}.ngx-record-editor__header-toggle{display:flex;align-items:center;gap:8px;font-size:var(--ngx-label-size, 13px);font-weight:var(--ngx-label-weight, 600);color:var(--ngx-color-text, #374151)}.ngx-record-editor__header-toggle-text{font-weight:600}.ngx-record-editor__banner{padding:11px 14px;border-radius:var(--ngx-radius-sm, 8px);font-size:var(--ngx-font-size, 13px);border:1px solid transparent;border-left-width:3px}.ngx-record-editor__banner--info{background:var(--ngx-color-info-soft, #eff6ff);border-color:var(--ngx-color-border, #dbeafe);border-left-color:var(--ngx-color-info, #1d4ed8);color:var(--ngx-color-info, #1d4ed8);display:flex;align-items:center;justify-content:space-between;gap:12px}.ngx-record-editor__banner--error{background:var(--ngx-color-error-soft, #fef2f2);border-color:var(--ngx-color-border, #fecaca);border-left-color:var(--ngx-color-error, #b91c1c);color:var(--ngx-color-error, #b91c1c)}.ngx-record-editor__banner--warning{background:var(--ngx-color-warning-soft, #fffbeb);border-color:var(--ngx-color-border, #fef3c7);border-left-color:var(--ngx-color-warning, #b45309);color:var(--ngx-color-warning, #b45309)}.ngx-record-editor__banner-dismiss{background:none;border:none;cursor:pointer;color:inherit;font-size:14px;line-height:1;padding:2px 6px;border-radius:4px}.ngx-record-editor__banner-dismiss:hover{background:#0000000f}.ngx-record-editor__section-bar{display:flex;align-items:center;justify-content:flex-end;flex-wrap:wrap;gap:8px}.ngx-record-editor__section-hint{margin-right:auto;font-size:var(--ngx-hint-size, 12px);color:var(--ngx-color-muted, #6b7280)}.ngx-record-editor__rows{border:1px solid var(--ngx-color-border, #f3f4f6);border-radius:var(--ngx-radius-sm, 8px);padding:12px 14px;display:flex;flex-direction:column;gap:6px}.ngx-record-editor__rows-head{display:flex;align-items:center;justify-content:space-between;gap:8px}.ngx-record-editor__row{display:flex;align-items:center;gap:8px;padding:8px 10px;border-radius:var(--ngx-radius-sm, 6px);background:var(--ngx-color-surface-alt, #f9fafb)}.ngx-record-editor__row-text{flex:1;font-size:var(--ngx-font-size, 13px);overflow-wrap:anywhere}.ngx-record-editor__row-btn{background:var(--ngx-color-surface, none);border:1px solid var(--ngx-color-border, #e5e7eb);border-radius:var(--ngx-radius-sm, 6px);cursor:pointer;font:inherit;font-size:var(--ngx-hint-size, 12px);font-weight:var(--ngx-label-weight, 600);color:inherit;padding:4px 10px}.ngx-record-editor__row-btn:hover{border-color:var(--ngx-color-accent, #e5e7eb);color:var(--ngx-color-accent, inherit)}.ngx-record-editor__row-empty{font-size:var(--ngx-hint-size, 12px);color:var(--ngx-color-muted, #6b7280);margin:0}.ngx-record-editor__drawer{border:1px solid var(--ngx-color-border, #dbeafe);background:var(--ngx-color-surface-alt, #f8fafc);border-radius:var(--ngx-radius, 8px);padding:16px;display:flex;flex-direction:column;gap:12px}.ngx-record-editor__summary-panel{background:var(--ngx-color-surface-alt, #f9fafb);border:1px solid var(--ngx-color-border, #f3f4f6);border-radius:var(--ngx-radius, 8px);padding:12px 16px;display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:4px var(--ngx-gap, 12px)}.ngx-record-editor__summary-item{display:flex;flex-direction:column;cursor:pointer;padding:6px 10px;border-radius:var(--ngx-radius-sm, 6px);min-width:0;transition:background .15s}.ngx-record-editor__summary-item:hover{background:var(--ngx-color-accent-soft, #eff6ff)}.ngx-record-editor__summary-label{font-size:11px;text-transform:uppercase;letter-spacing:.05em;color:var(--ngx-color-muted, #6b7280);font-weight:600}.ngx-record-editor__summary-val{font-size:var(--ngx-font-size, 14px);font-weight:600;color:var(--ngx-color-text, #1f2937);overflow-wrap:anywhere}@media(prefers-reduced-motion:reduce){.ngx-record-editor__summary-item{transition:none}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: DynamicFormComponent, selector: "ngx-dynamic-form", inputs: ["config", "rules", "initialData", "originalData", "userRoles", "language", "readonly", "readOnlyFields", "showInfoBanners", "loading", "error", "changeDebounceMs", "preview", "layout"], outputs: ["formSubmit", "formChange", "formReset", "activeTabChange", "saveRejected"] }, { kind: "component", type: DynamicFieldComponent, selector: "ngx-dynamic-field", inputs: ["field", "control", "config", "currentTabId", "language", "readonly", "userRoles"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
10005
10032
|
}
|
|
10006
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
10033
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: DynamicRecordFormComponent, decorators: [{
|
|
10007
10034
|
type: Component,
|
|
10008
10035
|
args: [{ changeDetection: ChangeDetectionStrategy.OnPush, selector: 'ngx-dynamic-record-form', standalone: true, imports: [CommonModule, ReactiveFormsModule, DynamicFormComponent, DynamicFieldComponent], template: "<div class=\"ngx-record-editor\">\n <!-- Record Header -->\n <div class=\"ngx-record-editor__header\">\n <div class=\"ngx-record-editor__title-group\">\n @if (profileImageUrl) {\n <img\n class=\"ngx-record-editor__avatar\"\n data-testid=\"header-profile-image\"\n [src]=\"profileImageUrl\"\n [alt]=\"recordHeading\"\n />\n } @else {\n <div class=\"ngx-record-editor__avatar\" data-testid=\"header-avatar-initial\">\n {{ avatarLetter }}\n </div>\n }\n <div>\n <!--\n The record's own name, with the entity underneath. These were the other way round \u2014\n both showing the entity \u2014 so the header never said which record was open.\n -->\n <h2 class=\"ngx-record-editor__title\" data-testid=\"record-title\">\n {{ recordHeading }}\n </h2>\n <span class=\"ngx-record-editor__subtitle\">{{ ui.text('entityLabel', language, { entity: recordTitle }) }}</span>\n </div>\n </div>\n\n <!-- Record-level status switch, outside section editing -->\n @if (headerToggleField; as toggleField) {\n <label class=\"ngx-record-editor__header-toggle\">\n <span>{{ formatFieldLabel(toggleField) }}</span>\n <input\n type=\"checkbox\"\n role=\"switch\"\n data-testid=\"header-toggle\"\n [checked]=\"headerToggleValue\"\n [disabled]=\"recordReadOnly\"\n [attr.aria-checked]=\"headerToggleValue\"\n (change)=\"toggleHeaderStatus()\"\n />\n <span class=\"ngx-record-editor__header-toggle-text\">{{ ui.text(headerToggleValue ? 'yes' : 'no', language) }}</span>\n </label>\n }\n </div>\n\n <!-- Dismissible info banners (rules `info` action) -->\n @for (banner of infoBanners; track banner.fieldId) {\n <div\n class=\"ngx-record-editor__banner ngx-record-editor__banner--info\"\n role=\"status\"\n [attr.data-testid]=\"'info-banner-' + banner.fieldId\"\n >\n <span>\u2139\uFE0F {{ banner.message }}</span>\n <button\n type=\"button\"\n class=\"ngx-record-editor__banner-dismiss\"\n [attr.data-testid]=\"'dismiss-' + banner.fieldId\"\n [attr.aria-label]=\"ui.text('dismissBanner', language, { message: banner.message })\"\n (click)=\"dismissInfoBanner(banner.fieldId)\"\n >\n \u2715\n </button>\n </div>\n }\n\n <!-- Modification baseline banner -->\n @if (isModified()) {\n <div class=\"ngx-record-editor__banner ngx-record-editor__banner--warning\">\n {{ ui.text('recordModified', language) }}\n </div>\n }\n\n <!-- Summary Panel (showOnMinimize fields with interactive jump) -->\n @if (summaryFields().length > 0) {\n <div class=\"ngx-record-editor__summary-panel\" data-testid=\"summary-panel\">\n @for (sf of summaryFields(); track sf.id) {\n <div\n class=\"ngx-record-editor__summary-item\"\n [attr.data-testid]=\"'summary-' + sf.id\"\n (click)=\"jumpToField(sf.id)\"\n [title]=\"ui.text('jumpToField', language, { field: formatFieldLabel(sf) })\"\n >\n <span class=\"ngx-record-editor__summary-label\">{{ formatFieldLabel(sf) }}</span>\n <span class=\"ngx-record-editor__summary-val\">{{ formatFieldValue(sf) }}</span>\n </div>\n }\n </div>\n }\n\n <!-- Section edit bar: one tab is edited at a time, and validated on its own -->\n @if (viewMode && !recordReadOnly) {\n <div class=\"ngx-record-editor__section-bar\">\n @if (isEditingActiveTab) {\n <span class=\"ngx-record-editor__section-hint\">{{ ui.text('editingSection', language) }}</span>\n <button type=\"button\" class=\"ngx-form__reset\" data-testid=\"cancel-section\" (click)=\"cancelSection()\">\n {{ ui.text('cancel', language) }}\n </button>\n <button type=\"button\" class=\"ngx-form__submit\" data-testid=\"save-section\" (click)=\"saveSection()\">\n {{ ui.text('saveSection', language) }}\n </button>\n } @else {\n <button type=\"button\" class=\"ngx-form__reset\" data-testid=\"edit-section\" (click)=\"editSection()\">\n {{ ui.text('editSection', language) }}\n </button>\n }\n </div>\n }\n\n @if (Object.keys(sectionErrors()).length > 0) {\n <div class=\"ngx-record-editor__banner ngx-record-editor__banner--error\" role=\"alert\" data-testid=\"section-errors\">\n @for (key of Object.keys(sectionErrors()); track key) {\n <div>\u26A0\uFE0F {{ sectionErrors()[key] }}</div>\n }\n </div>\n }\n\n <!-- Array rows: listed here, edited in the drawer below -->\n @for (arrayField of arrayFieldsForActiveTab; track arrayField.id) {\n <div class=\"ngx-record-editor__rows\" [attr.data-testid]=\"'rows-' + arrayField.id\">\n <div class=\"ngx-record-editor__rows-head\">\n <span class=\"ngx-record-editor__summary-label\">{{ formatFieldLabel(arrayField) }}</span>\n @if (!sectionReadOnly) {\n <button\n type=\"button\"\n class=\"ngx-form__reset\"\n [attr.data-testid]=\"'add-row-' + arrayField.id\"\n (click)=\"openAddRow(arrayField)\"\n >\n {{ ui.text('addRow', language) }}\n </button>\n }\n </div>\n\n @for (row of rowsOf(arrayField); track $index) {\n <div class=\"ngx-record-editor__row\" [attr.data-testid]=\"'row-' + arrayField.id + '-' + $index\">\n <span class=\"ngx-record-editor__row-text\">{{ rowSummary(arrayField, row) }}</span>\n @if (!sectionReadOnly) {\n <button\n type=\"button\"\n class=\"ngx-record-editor__row-btn\"\n [attr.data-testid]=\"'edit-row-' + arrayField.id + '-' + $index\"\n (click)=\"openEditRow(arrayField, $index)\"\n >\n {{ ui.text('edit', language) }}\n </button>\n <button\n type=\"button\"\n class=\"ngx-record-editor__row-btn\"\n [attr.data-testid]=\"'delete-row-' + arrayField.id + '-' + $index\"\n (click)=\"deleteRow(arrayField, $index)\"\n >\n {{ ui.text('delete', language) }}\n </button>\n }\n </div>\n } @empty {\n <p class=\"ngx-record-editor__row-empty\">{{ ui.text('noRows', language) }}</p>\n }\n </div>\n }\n\n <!--\n Row drawer. Deliberately outside the tab panel: building a row's FormGroup inside the\n tab view races the tab's own initialisation (the reference hit this with FormArray).\n -->\n @if (inlineRowField(); as rowField) {\n <div class=\"ngx-record-editor__drawer\" data-testid=\"row-drawer\" [formGroup]=\"inlineRowForm!\">\n <div class=\"ngx-record-editor__rows-head\">\n <strong>{{ rowDrawerTitle(rowField) }}</strong>\n </div>\n\n <div class=\"ngx-form__panel\">\n @for (child of rowField.children ?? []; track child.id) {\n <ngx-dynamic-field\n [field]=\"child\"\n [control]=\"inlineRowForm!.get(child.id)!\"\n [config]=\"config\"\n [language]=\"language\"\n [readonly]=\"false\"\n [userRoles]=\"userRoles\"\n [style.gridColumn]=\"'span 12'\"\n />\n }\n </div>\n\n <div class=\"ngx-record-editor__section-bar\">\n <button type=\"button\" class=\"ngx-form__reset\" data-testid=\"cancel-row\" (click)=\"cancelRow()\">\n {{ ui.text('cancel', language) }}\n </button>\n <button type=\"button\" class=\"ngx-form__submit\" data-testid=\"save-row\" (click)=\"saveRow()\">\n {{ ui.text('saveRow', language) }}\n </button>\n </div>\n </div>\n }\n\n <!-- Main Dynamic Form -->\n <ngx-dynamic-form\n [config]=\"config\"\n [rules]=\"rules\"\n [initialData]=\"initialData\"\n [originalData]=\"originalBaseline()\"\n [userRoles]=\"userRoles\"\n [language]=\"language\"\n [readonly]=\"sectionReadOnly\"\n [readOnlyFields]=\"readOnlyFields\"\n [layout]=\"layout\"\n [showInfoBanners]=\"false\"\n [loading]=\"loading\"\n [error]=\"error\"\n (formChange)=\"onFormChange($event)\"\n (formSubmit)=\"onFormSubmit($event)\"\n (saveRejected)=\"saveRejected.emit($event)\"\n (formReset)=\"onFormReset()\"\n (activeTabChange)=\"onActiveTabChange($event)\"\n />\n</div>\n", styles: [".ngx-record-editor{display:flex;flex-direction:column;gap:var(--ngx-gap, 16px);background:var(--ngx-color-surface, #ffffff);border:1px solid var(--ngx-color-border, #e5e7eb);border-radius:var(--ngx-radius, 12px);padding:20px}.ngx-record-editor__header{display:flex;align-items:center;justify-content:space-between;gap:14px;padding-bottom:var(--ngx-gap-sm, 16px);border-bottom:1px solid var(--ngx-color-border, #f3f4f6)}.ngx-record-editor__title-group{display:flex;align-items:center;gap:12px;min-width:0}.ngx-record-editor__avatar{width:44px;height:44px;border-radius:50%;object-fit:cover;background:var(--ngx-color-accent-soft, #e0e7ff);color:var(--ngx-color-accent, #4f46e5);border:1px solid var(--ngx-color-border, transparent);display:flex;align-items:center;justify-content:center;font-weight:700;font-size:17px;flex:none}.ngx-record-editor__title{font-size:19px;font-weight:700;color:var(--ngx-color-text, #111827);margin:0;letter-spacing:-.01em}.ngx-record-editor__subtitle{font-size:var(--ngx-hint-size, 13px);color:var(--ngx-color-muted, #6b7280)}.ngx-record-editor__header-toggle{display:flex;align-items:center;gap:8px;font-size:var(--ngx-label-size, 13px);font-weight:var(--ngx-label-weight, 600);color:var(--ngx-color-text, #374151)}.ngx-record-editor__header-toggle-text{font-weight:600}.ngx-record-editor__banner{padding:11px 14px;border-radius:var(--ngx-radius-sm, 8px);font-size:var(--ngx-font-size, 13px);border:1px solid transparent;border-left-width:3px}.ngx-record-editor__banner--info{background:var(--ngx-color-info-soft, #eff6ff);border-color:var(--ngx-color-border, #dbeafe);border-left-color:var(--ngx-color-info, #1d4ed8);color:var(--ngx-color-info, #1d4ed8);display:flex;align-items:center;justify-content:space-between;gap:12px}.ngx-record-editor__banner--error{background:var(--ngx-color-error-soft, #fef2f2);border-color:var(--ngx-color-border, #fecaca);border-left-color:var(--ngx-color-error, #b91c1c);color:var(--ngx-color-error, #b91c1c)}.ngx-record-editor__banner--warning{background:var(--ngx-color-warning-soft, #fffbeb);border-color:var(--ngx-color-border, #fef3c7);border-left-color:var(--ngx-color-warning, #b45309);color:var(--ngx-color-warning, #b45309)}.ngx-record-editor__banner-dismiss{background:none;border:none;cursor:pointer;color:inherit;font-size:14px;line-height:1;padding:2px 6px;border-radius:4px}.ngx-record-editor__banner-dismiss:hover{background:#0000000f}.ngx-record-editor__section-bar{display:flex;align-items:center;justify-content:flex-end;flex-wrap:wrap;gap:8px}.ngx-record-editor__section-hint{margin-right:auto;font-size:var(--ngx-hint-size, 12px);color:var(--ngx-color-muted, #6b7280)}.ngx-record-editor__rows{border:1px solid var(--ngx-color-border, #f3f4f6);border-radius:var(--ngx-radius-sm, 8px);padding:12px 14px;display:flex;flex-direction:column;gap:6px}.ngx-record-editor__rows-head{display:flex;align-items:center;justify-content:space-between;gap:8px}.ngx-record-editor__row{display:flex;align-items:center;gap:8px;padding:8px 10px;border-radius:var(--ngx-radius-sm, 6px);background:var(--ngx-color-surface-alt, #f9fafb)}.ngx-record-editor__row-text{flex:1;font-size:var(--ngx-font-size, 13px);overflow-wrap:anywhere}.ngx-record-editor__row-btn{background:var(--ngx-color-surface, none);border:1px solid var(--ngx-color-border, #e5e7eb);border-radius:var(--ngx-radius-sm, 6px);cursor:pointer;font:inherit;font-size:var(--ngx-hint-size, 12px);font-weight:var(--ngx-label-weight, 600);color:inherit;padding:4px 10px}.ngx-record-editor__row-btn:hover{border-color:var(--ngx-color-accent, #e5e7eb);color:var(--ngx-color-accent, inherit)}.ngx-record-editor__row-empty{font-size:var(--ngx-hint-size, 12px);color:var(--ngx-color-muted, #6b7280);margin:0}.ngx-record-editor__drawer{border:1px solid var(--ngx-color-border, #dbeafe);background:var(--ngx-color-surface-alt, #f8fafc);border-radius:var(--ngx-radius, 8px);padding:16px;display:flex;flex-direction:column;gap:12px}.ngx-record-editor__summary-panel{background:var(--ngx-color-surface-alt, #f9fafb);border:1px solid var(--ngx-color-border, #f3f4f6);border-radius:var(--ngx-radius, 8px);padding:12px 16px;display:grid;grid-template-columns:repeat(auto-fit,minmax(190px,1fr));gap:4px var(--ngx-gap, 12px)}.ngx-record-editor__summary-item{display:flex;flex-direction:column;cursor:pointer;padding:6px 10px;border-radius:var(--ngx-radius-sm, 6px);min-width:0;transition:background .15s}.ngx-record-editor__summary-item:hover{background:var(--ngx-color-accent-soft, #eff6ff)}.ngx-record-editor__summary-label{font-size:11px;text-transform:uppercase;letter-spacing:.05em;color:var(--ngx-color-muted, #6b7280);font-weight:600}.ngx-record-editor__summary-val{font-size:var(--ngx-font-size, 14px);font-weight:600;color:var(--ngx-color-text, #1f2937);overflow-wrap:anywhere}@media(prefers-reduced-motion:reduce){.ngx-record-editor__summary-item{transition:none}}\n"] }]
|
|
10009
10036
|
}], propDecorators: { config: [{
|
|
@@ -10086,7 +10113,10 @@ const defaultSheetParser = async (file) => {
|
|
|
10086
10113
|
throw new Error(`Cannot read "${file.name}". The built-in parser handles CSV only — register a ` +
|
|
10087
10114
|
'sheetParser with provideNgxDynamicEntity({ sheetParser }) to accept other formats.');
|
|
10088
10115
|
}
|
|
10089
|
-
|
|
10116
|
+
// A `.tsv` says what it is. Anything else is read the way `parseCsv` reads text — the
|
|
10117
|
+
// separator its header line uses most — which is also how the server reads an upload.
|
|
10118
|
+
const tabs = /\.tsv$/i.test(file?.name ?? '');
|
|
10119
|
+
return parseCsv(await file.text(), tabs ? { delimiter: '\t' } : {});
|
|
10090
10120
|
};
|
|
10091
10121
|
|
|
10092
10122
|
/**
|
|
@@ -10134,12 +10164,18 @@ class LocalImportTransport {
|
|
|
10134
10164
|
}
|
|
10135
10165
|
async preview(file, context) {
|
|
10136
10166
|
const sheet = await this.read(file);
|
|
10137
|
-
|
|
10167
|
+
// Sized from the headers exactly as the server sizes them, so both offer the same slots.
|
|
10168
|
+
const arrayBound = arrayBoundFor(sheet.headers, context.config, null, context.lang);
|
|
10169
|
+
const { columns } = deriveImportColumns(context.config, { lang: context.lang, maxArrayRows: arrayBound });
|
|
10138
10170
|
return {
|
|
10139
10171
|
headers: sheet.headers,
|
|
10140
|
-
|
|
10172
|
+
// Rendered the way the server renders it, so a typed date shows — and previews — as the
|
|
10173
|
+
// calendar date the commit will store, not as a timestamp.
|
|
10174
|
+
sample: sheet.rows.slice(0, SAMPLE_ROWS).map(row => row.map(cellText)),
|
|
10141
10175
|
rowCount: sheet.rows.length,
|
|
10142
10176
|
suggestion: suggestMapping(sheet.headers, columns, context.config?.entity ?? ''),
|
|
10177
|
+
arrayBound,
|
|
10178
|
+
...(sheet.delimiter ? { delimiter: sheet.delimiter } : {}),
|
|
10143
10179
|
};
|
|
10144
10180
|
}
|
|
10145
10181
|
async commit(file, plan, context) {
|
|
@@ -10148,6 +10184,8 @@ class LocalImportTransport {
|
|
|
10148
10184
|
lang: context.lang,
|
|
10149
10185
|
rules: context.rules,
|
|
10150
10186
|
lookups: context.lookups,
|
|
10187
|
+
// A `;` file means `1,5` is one and a half — the same call the server makes.
|
|
10188
|
+
decimal: decimalMarkFor(sheet.delimiter),
|
|
10151
10189
|
});
|
|
10152
10190
|
}
|
|
10153
10191
|
async template(spec, format) {
|
|
@@ -10169,10 +10207,10 @@ class LocalImportTransport {
|
|
|
10169
10207
|
const csv = toCsv(spec.columns.map(column => column.header));
|
|
10170
10208
|
return new Blob([csv], { type: 'text/csv;charset=utf-8' });
|
|
10171
10209
|
}
|
|
10172
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
10173
|
-
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.
|
|
10210
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: LocalImportTransport, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
10211
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: LocalImportTransport, providedIn: 'root' });
|
|
10174
10212
|
}
|
|
10175
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
10213
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: LocalImportTransport, decorators: [{
|
|
10176
10214
|
type: Injectable,
|
|
10177
10215
|
args: [{ providedIn: 'root' }]
|
|
10178
10216
|
}] });
|
|
@@ -10309,6 +10347,9 @@ class HttpImportTransport {
|
|
|
10309
10347
|
sample: preview.sample ?? [],
|
|
10310
10348
|
suggestion: preview.suggestion,
|
|
10311
10349
|
rowCount: preview.rowCount ?? 0,
|
|
10350
|
+
// A server older than 2.3 does not say, and always sized for the default.
|
|
10351
|
+
arrayBound: preview.arrayBound ?? DEFAULT_ARRAY_ROWS,
|
|
10352
|
+
...(preview.delimiter ? { delimiter: preview.delimiter } : {}),
|
|
10312
10353
|
};
|
|
10313
10354
|
}
|
|
10314
10355
|
async commit(file, plan, context) {
|
|
@@ -10396,8 +10437,8 @@ class ImportErrorsComponent {
|
|
|
10396
10437
|
.sort((a, b) => a[0] - b[0])
|
|
10397
10438
|
.map(([row, problems]) => ({ row, problems }));
|
|
10398
10439
|
}
|
|
10399
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
10400
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.
|
|
10440
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: ImportErrorsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
10441
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.25", type: ImportErrorsComponent, isStandalone: true, selector: "ngx-import-errors", inputs: { errors: "errors", language: "language" }, ngImport: i0, template: `
|
|
10401
10442
|
@if (errors.length) {
|
|
10402
10443
|
<section class="ngx-import-errors" data-testid="import-errors">
|
|
10403
10444
|
<h4 class="ngx-import-errors__heading">{{ ui.text('importErrorsHeading', language) }}</h4>
|
|
@@ -10429,7 +10470,7 @@ class ImportErrorsComponent {
|
|
|
10429
10470
|
}
|
|
10430
10471
|
`, isInline: true, styles: [".ngx-import-errors__heading{margin:0 0 8px;font-size:var(--ngx-font-size, 14px);color:var(--ngx-color-error, #b91c1c)}.ngx-import-errors__table{width:100%;border-collapse:collapse}.ngx-import-errors__table th,.ngx-import-errors__table td{text-align:left;padding:6px 10px;border-bottom:1px solid var(--ngx-color-border, #e5e7eb);vertical-align:top}.ngx-import-errors__table thead th{color:var(--ngx-color-muted, #6b7280)}.ngx-import-errors__table tbody th{white-space:nowrap;color:var(--ngx-color-error, #b91c1c)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
10431
10472
|
}
|
|
10432
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
10473
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: ImportErrorsComponent, decorators: [{
|
|
10433
10474
|
type: Component,
|
|
10434
10475
|
args: [{ selector: 'ngx-import-errors', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [], template: `
|
|
10435
10476
|
@if (errors.length) {
|
|
@@ -10468,6 +10509,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.24", ngImpo
|
|
|
10468
10509
|
type: Input
|
|
10469
10510
|
}] } });
|
|
10470
10511
|
|
|
10512
|
+
/** The select's value for "keep the plan's fixed value", which is not a column. */
|
|
10513
|
+
const CONSTANT = -1;
|
|
10471
10514
|
/**
|
|
10472
10515
|
* Match the sheet's columns to the config's fields.
|
|
10473
10516
|
*
|
|
@@ -10497,18 +10540,70 @@ class ImportMapperComponent {
|
|
|
10497
10540
|
* attachment column was never going to arrive.
|
|
10498
10541
|
*/
|
|
10499
10542
|
unsupported = signal(0, ...(ngDevMode ? [{ debugName: "unsupported" }] : /* istanbul ignore next */ []));
|
|
10543
|
+
/** A plan entry whose field this config no longer has. Kept, and emitted, until removed. */
|
|
10544
|
+
stale = signal([], ...(ngDevMode ? [{ debugName: "stale" }] : /* istanbul ignore next */ []));
|
|
10545
|
+
/** Repeating fields the user can add a row to. */
|
|
10546
|
+
arrays = signal([], ...(ngDevMode ? [{ debugName: "arrays" }] : /* istanbul ignore next */ []));
|
|
10547
|
+
constant = CONSTANT;
|
|
10548
|
+
/** Rows added by hand per repeating field, on top of what the sheet and plan imply. */
|
|
10549
|
+
extraSlots = new Map();
|
|
10550
|
+
/** The incoming plan, read through the 2.2 alias. Every entry in it survives `toPlan`. */
|
|
10551
|
+
incoming = null;
|
|
10500
10552
|
ngOnChanges() {
|
|
10501
|
-
|
|
10553
|
+
this.incoming = this.plan ? upgradeLegacyRefs(this.plan, this.config) : null;
|
|
10554
|
+
this.extraSlots.clear();
|
|
10555
|
+
this.rows.set([]);
|
|
10556
|
+
this.rebuild();
|
|
10557
|
+
}
|
|
10558
|
+
/**
|
|
10559
|
+
* Rows for every field, sized so nothing the plan or the sheet names is out of reach.
|
|
10560
|
+
*
|
|
10561
|
+
* Sizing from the default three meant a sheet with `Phone 4` could not be mapped at all,
|
|
10562
|
+
* and a stored plan reaching further lost those entries on the next change — `toPlan` only
|
|
10563
|
+
* ever emitted rows, and there were none for them.
|
|
10564
|
+
*/
|
|
10565
|
+
rebuild() {
|
|
10566
|
+
const entries = (this.incoming?.entries ?? []).filter(Boolean);
|
|
10567
|
+
const base = arrayBoundFor(this.headers, this.config, this.incoming, this.language);
|
|
10568
|
+
const extra = Math.max(0, ...this.extraSlots.values());
|
|
10569
|
+
// Readonly and system fields are derived so a plan that maps one keeps it, but only
|
|
10570
|
+
// offered as a row when the plan does — nobody maps a column onto a field the form fills.
|
|
10571
|
+
const { columns, unsupported } = deriveImportColumns(this.config, {
|
|
10572
|
+
lang: this.language,
|
|
10573
|
+
maxArrayRows: base + extra,
|
|
10574
|
+
includeReadonly: true,
|
|
10575
|
+
includeSystemDefault: true,
|
|
10576
|
+
});
|
|
10502
10577
|
this.unsupported.set(unsupported.length);
|
|
10503
|
-
const byRef = new Map(
|
|
10504
|
-
|
|
10578
|
+
const byRef = new Map(entries.map(entry => [entry.ref, entry]));
|
|
10579
|
+
const known = new Set(columns.map(column => column.ref));
|
|
10580
|
+
const previous = new Map(this.rows().map(row => [row.column.ref, row]));
|
|
10581
|
+
const offered = columns.filter(column => (byRef.has(column.ref) || (!column.field.readonly && !column.field.systemDefault)) &&
|
|
10582
|
+
(column.arrayRef === undefined ||
|
|
10583
|
+
byRef.has(column.ref) ||
|
|
10584
|
+
column.arrayIndex < base + (this.extraSlots.get(column.arrayRef) ?? 0)));
|
|
10585
|
+
this.rows.set(offered.map(column => {
|
|
10586
|
+
// A choice already made survives a rebuild for an added slot.
|
|
10587
|
+
const kept = previous.get(column.ref);
|
|
10588
|
+
if (kept)
|
|
10589
|
+
return { ...kept, column };
|
|
10505
10590
|
const entry = byRef.get(column.ref);
|
|
10506
10591
|
return {
|
|
10507
10592
|
column,
|
|
10508
|
-
source: entry?.column ?? null,
|
|
10593
|
+
source: entry?.column ?? (entry?.constant !== undefined ? CONSTANT : null),
|
|
10509
10594
|
guessed: entry?.confidence === 'guess',
|
|
10595
|
+
entry,
|
|
10596
|
+
dirty: false,
|
|
10510
10597
|
};
|
|
10511
10598
|
}));
|
|
10599
|
+
this.stale.set(entries.filter(entry => !known.has(entry.ref)));
|
|
10600
|
+
const arrays = new Map();
|
|
10601
|
+
for (const column of offered) {
|
|
10602
|
+
if (column.arrayRef !== undefined && !arrays.has(column.arrayRef)) {
|
|
10603
|
+
arrays.set(column.arrayRef, column.arrayLabel ?? column.arrayRef);
|
|
10604
|
+
}
|
|
10605
|
+
}
|
|
10606
|
+
this.arrays.set([...arrays].map(([arrayRef, label]) => ({ arrayRef, label })));
|
|
10512
10607
|
}
|
|
10513
10608
|
mappedCount() {
|
|
10514
10609
|
return this.rows().filter(row => row.source !== null).length;
|
|
@@ -10516,32 +10611,83 @@ class ImportMapperComponent {
|
|
|
10516
10611
|
missingRequired() {
|
|
10517
10612
|
return this.rows().filter(row => row.column.required && row.source === null).length;
|
|
10518
10613
|
}
|
|
10614
|
+
hasConstant(row) {
|
|
10615
|
+
return !!row.entry && row.entry.constant !== undefined && row.entry.column === undefined;
|
|
10616
|
+
}
|
|
10617
|
+
constantText(entry) {
|
|
10618
|
+
const value = entry.constant;
|
|
10619
|
+
return typeof value === 'string' ? value : JSON.stringify(value);
|
|
10620
|
+
}
|
|
10519
10621
|
choose(row, source) {
|
|
10520
10622
|
this.rows.update(rows => rows.map(candidate => candidate.column.ref === row.column.ref
|
|
10521
|
-
? // A hand-made choice is not a guess any more, so the badge goes.
|
|
10522
|
-
|
|
10623
|
+
? // A hand-made choice is not a guess any more, so the badge goes. Going back to
|
|
10624
|
+
// the plan's fixed value is going back to its entry, untouched.
|
|
10625
|
+
{ ...candidate, source, guessed: false, dirty: source !== CONSTANT }
|
|
10523
10626
|
: candidate));
|
|
10524
10627
|
this.planChange.emit(this.toPlan());
|
|
10525
10628
|
}
|
|
10526
|
-
/**
|
|
10629
|
+
/** One more row for one repeating field, for this session. */
|
|
10630
|
+
addSlot(arrayRef) {
|
|
10631
|
+
this.extraSlots.set(arrayRef, (this.extraSlots.get(arrayRef) ?? 0) + 1);
|
|
10632
|
+
this.rebuild();
|
|
10633
|
+
}
|
|
10634
|
+
removeStale(entry) {
|
|
10635
|
+
this.stale.update(stale => stale.filter(candidate => candidate !== entry));
|
|
10636
|
+
this.planChange.emit(this.toPlan());
|
|
10637
|
+
}
|
|
10638
|
+
/**
|
|
10639
|
+
* The current selections as the plan that will be applied.
|
|
10640
|
+
*
|
|
10641
|
+
* Every entry of the incoming plan survives in its own position — a fixed value, a ref the
|
|
10642
|
+
* config no longer has — unless the user changed or removed it, and an untouched entry is
|
|
10643
|
+
* emitted exactly as it arrived. New selections follow, in field order.
|
|
10644
|
+
*/
|
|
10527
10645
|
toPlan() {
|
|
10528
|
-
const
|
|
10529
|
-
|
|
10530
|
-
|
|
10531
|
-
|
|
10532
|
-
|
|
10533
|
-
|
|
10534
|
-
|
|
10535
|
-
|
|
10646
|
+
const rowByRef = new Map(this.rows().map(row => [row.column.ref, row]));
|
|
10647
|
+
const stale = new Set(this.stale());
|
|
10648
|
+
const emitted = new Set();
|
|
10649
|
+
const entries = [];
|
|
10650
|
+
const entryOf = (row) => {
|
|
10651
|
+
if (!row.dirty && row.entry)
|
|
10652
|
+
return row.entry;
|
|
10653
|
+
if (row.source === null || row.source === CONSTANT)
|
|
10654
|
+
return null;
|
|
10655
|
+
return {
|
|
10656
|
+
ref: row.column.ref,
|
|
10657
|
+
column: row.source,
|
|
10658
|
+
header: this.headers[row.source],
|
|
10659
|
+
confidence: row.guessed ? 'guess' : 'exact',
|
|
10660
|
+
};
|
|
10661
|
+
};
|
|
10662
|
+
for (const original of this.incoming?.entries ?? []) {
|
|
10663
|
+
const row = original ? rowByRef.get(original.ref) : undefined;
|
|
10664
|
+
if (row) {
|
|
10665
|
+
emitted.add(row.column.ref);
|
|
10666
|
+
const entry = entryOf(row);
|
|
10667
|
+
if (entry)
|
|
10668
|
+
entries.push(entry);
|
|
10669
|
+
}
|
|
10670
|
+
else if (stale.has(original)) {
|
|
10671
|
+
entries.push(original);
|
|
10672
|
+
}
|
|
10673
|
+
}
|
|
10674
|
+
for (const row of this.rows()) {
|
|
10675
|
+
if (emitted.has(row.column.ref))
|
|
10676
|
+
continue;
|
|
10677
|
+
const entry = entryOf(row);
|
|
10678
|
+
if (entry)
|
|
10679
|
+
entries.push(entry);
|
|
10680
|
+
}
|
|
10536
10681
|
return {
|
|
10682
|
+
...this.incoming,
|
|
10537
10683
|
entity: this.config?.entity ?? '',
|
|
10538
10684
|
configVersion: this.config?.version,
|
|
10539
10685
|
sourceHeaders: [...this.headers],
|
|
10540
10686
|
entries,
|
|
10541
10687
|
};
|
|
10542
10688
|
}
|
|
10543
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
10544
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.
|
|
10689
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: ImportMapperComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
10690
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.25", type: ImportMapperComponent, isStandalone: true, selector: "ngx-import-mapper", inputs: { config: "config", headers: "headers", plan: "plan", language: "language" }, outputs: { planChange: "planChange" }, usesOnChanges: true, ngImport: i0, template: `
|
|
10545
10691
|
<section class="ngx-import-mapper" data-testid="import-mapper">
|
|
10546
10692
|
<p class="ngx-import-mapper__summary" data-testid="import-mapped-count">
|
|
10547
10693
|
{{ ui.text('importMappedCount', language, { mapped: mappedCount(), total: rows().length }) }}
|
|
@@ -10582,6 +10728,11 @@ class ImportMapperComponent {
|
|
|
10582
10728
|
(ngModelChange)="choose(row, $event)"
|
|
10583
10729
|
>
|
|
10584
10730
|
<option [ngValue]="null">{{ ui.text('importUnmapped', language) }}</option>
|
|
10731
|
+
@if (hasConstant(row)) {
|
|
10732
|
+
<option [ngValue]="constant">
|
|
10733
|
+
{{ ui.text('importConstant', language, { value: constantText(row.entry!) }) }}
|
|
10734
|
+
</option>
|
|
10735
|
+
}
|
|
10585
10736
|
@for (header of headers; track $index) {
|
|
10586
10737
|
<option [ngValue]="$index">{{ header || '—' }}</option>
|
|
10587
10738
|
}
|
|
@@ -10596,10 +10747,49 @@ class ImportMapperComponent {
|
|
|
10596
10747
|
}
|
|
10597
10748
|
</tbody>
|
|
10598
10749
|
</table>
|
|
10750
|
+
|
|
10751
|
+
@if (arrays().length) {
|
|
10752
|
+
<p class="ngx-import-mapper__slots">
|
|
10753
|
+
@for (array of arrays(); track array.arrayRef) {
|
|
10754
|
+
<button
|
|
10755
|
+
type="button"
|
|
10756
|
+
class="ngx-import-mapper__add-slot"
|
|
10757
|
+
[attr.data-testid]="'import-add-slot-' + array.arrayRef"
|
|
10758
|
+
(click)="addSlot(array.arrayRef)"
|
|
10759
|
+
>
|
|
10760
|
+
{{ ui.text('importAddSlot', language, { label: array.label }) }}
|
|
10761
|
+
</button>
|
|
10762
|
+
}
|
|
10763
|
+
</p>
|
|
10764
|
+
}
|
|
10765
|
+
|
|
10766
|
+
@if (stale().length) {
|
|
10767
|
+
<div class="ngx-import-mapper__stale" data-testid="import-stale" role="alert">
|
|
10768
|
+
<p>
|
|
10769
|
+
<strong>{{ ui.text('importStaleHeading', language) }}</strong>
|
|
10770
|
+
{{ ui.text('importStaleExplain', language) }}
|
|
10771
|
+
</p>
|
|
10772
|
+
<ul>
|
|
10773
|
+
@for (entry of stale(); track entry.ref) {
|
|
10774
|
+
<li [attr.data-testid]="'import-stale-' + entry.ref">
|
|
10775
|
+
<code>{{ entry.ref }}</code>
|
|
10776
|
+
<button
|
|
10777
|
+
type="button"
|
|
10778
|
+
class="ngx-import-mapper__remove"
|
|
10779
|
+
[attr.data-testid]="'import-remove-' + entry.ref"
|
|
10780
|
+
(click)="removeStale(entry)"
|
|
10781
|
+
>
|
|
10782
|
+
{{ ui.text('importRemoveEntry', language) }}
|
|
10783
|
+
</button>
|
|
10784
|
+
</li>
|
|
10785
|
+
}
|
|
10786
|
+
</ul>
|
|
10787
|
+
</div>
|
|
10788
|
+
}
|
|
10599
10789
|
</section>
|
|
10600
|
-
`, isInline: true, styles: [".ngx-import-mapper__summary,.ngx-import-mapper__warning,.ngx-import-mapper__note{margin:0 0 10px}.ngx-import-mapper__warning{color:var(--ngx-color-warning, #b45309);background:var(--ngx-color-warning-soft, #fffbeb);border:1px solid var(--ngx-color-border, #fef3c7);border-radius:var(--ngx-radius-sm, 6px);padding:8px 12px}.ngx-import-mapper__note{color:var(--ngx-color-muted, #6b7280)}.ngx-import-mapper__table{width:100%;border-collapse:collapse}.ngx-import-mapper__table th,.ngx-import-mapper__table td{text-align:left;padding:8px 10px;border-bottom:1px solid var(--ngx-color-border, #e5e7eb);vertical-align:middle}.ngx-import-mapper__table thead th{color:var(--ngx-color-muted, #6b7280);font-size:var(--ngx-font-size, 13px);text-transform:uppercase;letter-spacing:.04em}.ngx-import-mapper__required{color:var(--ngx-color-error, #b91c1c);margin-left:2px}.ngx-import-mapper__select{min-width:220px;max-width:100%;padding:6px 8px;border:1px solid var(--ngx-color-border, #d1d5db);border-radius:var(--ngx-radius-sm, 6px);background:var(--ngx-color-surface, #ffffff);color:inherit;font:inherit}.ngx-import-mapper__guess{margin-left:8px;padding:2px 8px;border-radius:999px;font-size:12px;color:var(--ngx-color-info, #1d4ed8);background:var(--ngx-color-info-soft, #eff6ff)}@media(max-width:640px){.ngx-import-mapper__select{min-width:0;width:100%}}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
10790
|
+
`, isInline: true, styles: [".ngx-import-mapper__summary,.ngx-import-mapper__warning,.ngx-import-mapper__note{margin:0 0 10px}.ngx-import-mapper__warning{color:var(--ngx-color-warning, #b45309);background:var(--ngx-color-warning-soft, #fffbeb);border:1px solid var(--ngx-color-border, #fef3c7);border-radius:var(--ngx-radius-sm, 6px);padding:8px 12px}.ngx-import-mapper__note{color:var(--ngx-color-muted, #6b7280)}.ngx-import-mapper__table{width:100%;border-collapse:collapse}.ngx-import-mapper__table th,.ngx-import-mapper__table td{text-align:left;padding:8px 10px;border-bottom:1px solid var(--ngx-color-border, #e5e7eb);vertical-align:middle}.ngx-import-mapper__table thead th{color:var(--ngx-color-muted, #6b7280);font-size:var(--ngx-font-size, 13px);text-transform:uppercase;letter-spacing:.04em}.ngx-import-mapper__required{color:var(--ngx-color-error, #b91c1c);margin-left:2px}.ngx-import-mapper__select{min-width:220px;max-width:100%;padding:6px 8px;border:1px solid var(--ngx-color-border, #d1d5db);border-radius:var(--ngx-radius-sm, 6px);background:var(--ngx-color-surface, #ffffff);color:inherit;font:inherit}.ngx-import-mapper__slots{display:flex;flex-wrap:wrap;gap:8px;margin:10px 0 0}.ngx-import-mapper__add-slot,.ngx-import-mapper__remove{padding:4px 10px;border:1px solid var(--ngx-color-border, #d1d5db);border-radius:var(--ngx-radius-sm, 6px);background:var(--ngx-color-surface, #ffffff);color:inherit;font:inherit;cursor:pointer}.ngx-import-mapper__remove{margin-left:8px}.ngx-import-mapper__stale{margin-top:12px;padding:8px 12px;color:var(--ngx-color-error, #b91c1c);background:var(--ngx-color-error-soft, #fef2f2);border-radius:var(--ngx-radius-sm, 6px)}.ngx-import-mapper__stale p,.ngx-import-mapper__stale ul{margin:0 0 6px}.ngx-import-mapper__guess{margin-left:8px;padding:2px 8px;border-radius:999px;font-size:12px;color:var(--ngx-color-info, #1d4ed8);background:var(--ngx-color-info-soft, #eff6ff)}@media(max-width:640px){.ngx-import-mapper__select{min-width:0;width:100%}}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
10601
10791
|
}
|
|
10602
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
10792
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: ImportMapperComponent, decorators: [{
|
|
10603
10793
|
type: Component,
|
|
10604
10794
|
args: [{ selector: 'ngx-import-mapper', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [FormsModule], template: `
|
|
10605
10795
|
<section class="ngx-import-mapper" data-testid="import-mapper">
|
|
@@ -10642,6 +10832,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.24", ngImpo
|
|
|
10642
10832
|
(ngModelChange)="choose(row, $event)"
|
|
10643
10833
|
>
|
|
10644
10834
|
<option [ngValue]="null">{{ ui.text('importUnmapped', language) }}</option>
|
|
10835
|
+
@if (hasConstant(row)) {
|
|
10836
|
+
<option [ngValue]="constant">
|
|
10837
|
+
{{ ui.text('importConstant', language, { value: constantText(row.entry!) }) }}
|
|
10838
|
+
</option>
|
|
10839
|
+
}
|
|
10645
10840
|
@for (header of headers; track $index) {
|
|
10646
10841
|
<option [ngValue]="$index">{{ header || '—' }}</option>
|
|
10647
10842
|
}
|
|
@@ -10656,8 +10851,47 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.24", ngImpo
|
|
|
10656
10851
|
}
|
|
10657
10852
|
</tbody>
|
|
10658
10853
|
</table>
|
|
10854
|
+
|
|
10855
|
+
@if (arrays().length) {
|
|
10856
|
+
<p class="ngx-import-mapper__slots">
|
|
10857
|
+
@for (array of arrays(); track array.arrayRef) {
|
|
10858
|
+
<button
|
|
10859
|
+
type="button"
|
|
10860
|
+
class="ngx-import-mapper__add-slot"
|
|
10861
|
+
[attr.data-testid]="'import-add-slot-' + array.arrayRef"
|
|
10862
|
+
(click)="addSlot(array.arrayRef)"
|
|
10863
|
+
>
|
|
10864
|
+
{{ ui.text('importAddSlot', language, { label: array.label }) }}
|
|
10865
|
+
</button>
|
|
10866
|
+
}
|
|
10867
|
+
</p>
|
|
10868
|
+
}
|
|
10869
|
+
|
|
10870
|
+
@if (stale().length) {
|
|
10871
|
+
<div class="ngx-import-mapper__stale" data-testid="import-stale" role="alert">
|
|
10872
|
+
<p>
|
|
10873
|
+
<strong>{{ ui.text('importStaleHeading', language) }}</strong>
|
|
10874
|
+
{{ ui.text('importStaleExplain', language) }}
|
|
10875
|
+
</p>
|
|
10876
|
+
<ul>
|
|
10877
|
+
@for (entry of stale(); track entry.ref) {
|
|
10878
|
+
<li [attr.data-testid]="'import-stale-' + entry.ref">
|
|
10879
|
+
<code>{{ entry.ref }}</code>
|
|
10880
|
+
<button
|
|
10881
|
+
type="button"
|
|
10882
|
+
class="ngx-import-mapper__remove"
|
|
10883
|
+
[attr.data-testid]="'import-remove-' + entry.ref"
|
|
10884
|
+
(click)="removeStale(entry)"
|
|
10885
|
+
>
|
|
10886
|
+
{{ ui.text('importRemoveEntry', language) }}
|
|
10887
|
+
</button>
|
|
10888
|
+
</li>
|
|
10889
|
+
}
|
|
10890
|
+
</ul>
|
|
10891
|
+
</div>
|
|
10892
|
+
}
|
|
10659
10893
|
</section>
|
|
10660
|
-
`, styles: [".ngx-import-mapper__summary,.ngx-import-mapper__warning,.ngx-import-mapper__note{margin:0 0 10px}.ngx-import-mapper__warning{color:var(--ngx-color-warning, #b45309);background:var(--ngx-color-warning-soft, #fffbeb);border:1px solid var(--ngx-color-border, #fef3c7);border-radius:var(--ngx-radius-sm, 6px);padding:8px 12px}.ngx-import-mapper__note{color:var(--ngx-color-muted, #6b7280)}.ngx-import-mapper__table{width:100%;border-collapse:collapse}.ngx-import-mapper__table th,.ngx-import-mapper__table td{text-align:left;padding:8px 10px;border-bottom:1px solid var(--ngx-color-border, #e5e7eb);vertical-align:middle}.ngx-import-mapper__table thead th{color:var(--ngx-color-muted, #6b7280);font-size:var(--ngx-font-size, 13px);text-transform:uppercase;letter-spacing:.04em}.ngx-import-mapper__required{color:var(--ngx-color-error, #b91c1c);margin-left:2px}.ngx-import-mapper__select{min-width:220px;max-width:100%;padding:6px 8px;border:1px solid var(--ngx-color-border, #d1d5db);border-radius:var(--ngx-radius-sm, 6px);background:var(--ngx-color-surface, #ffffff);color:inherit;font:inherit}.ngx-import-mapper__guess{margin-left:8px;padding:2px 8px;border-radius:999px;font-size:12px;color:var(--ngx-color-info, #1d4ed8);background:var(--ngx-color-info-soft, #eff6ff)}@media(max-width:640px){.ngx-import-mapper__select{min-width:0;width:100%}}\n"] }]
|
|
10894
|
+
`, styles: [".ngx-import-mapper__summary,.ngx-import-mapper__warning,.ngx-import-mapper__note{margin:0 0 10px}.ngx-import-mapper__warning{color:var(--ngx-color-warning, #b45309);background:var(--ngx-color-warning-soft, #fffbeb);border:1px solid var(--ngx-color-border, #fef3c7);border-radius:var(--ngx-radius-sm, 6px);padding:8px 12px}.ngx-import-mapper__note{color:var(--ngx-color-muted, #6b7280)}.ngx-import-mapper__table{width:100%;border-collapse:collapse}.ngx-import-mapper__table th,.ngx-import-mapper__table td{text-align:left;padding:8px 10px;border-bottom:1px solid var(--ngx-color-border, #e5e7eb);vertical-align:middle}.ngx-import-mapper__table thead th{color:var(--ngx-color-muted, #6b7280);font-size:var(--ngx-font-size, 13px);text-transform:uppercase;letter-spacing:.04em}.ngx-import-mapper__required{color:var(--ngx-color-error, #b91c1c);margin-left:2px}.ngx-import-mapper__select{min-width:220px;max-width:100%;padding:6px 8px;border:1px solid var(--ngx-color-border, #d1d5db);border-radius:var(--ngx-radius-sm, 6px);background:var(--ngx-color-surface, #ffffff);color:inherit;font:inherit}.ngx-import-mapper__slots{display:flex;flex-wrap:wrap;gap:8px;margin:10px 0 0}.ngx-import-mapper__add-slot,.ngx-import-mapper__remove{padding:4px 10px;border:1px solid var(--ngx-color-border, #d1d5db);border-radius:var(--ngx-radius-sm, 6px);background:var(--ngx-color-surface, #ffffff);color:inherit;font:inherit;cursor:pointer}.ngx-import-mapper__remove{margin-left:8px}.ngx-import-mapper__stale{margin-top:12px;padding:8px 12px;color:var(--ngx-color-error, #b91c1c);background:var(--ngx-color-error-soft, #fef2f2);border-radius:var(--ngx-radius-sm, 6px)}.ngx-import-mapper__stale p,.ngx-import-mapper__stale ul{margin:0 0 6px}.ngx-import-mapper__guess{margin-left:8px;padding:2px 8px;border-radius:999px;font-size:12px;color:var(--ngx-color-info, #1d4ed8);background:var(--ngx-color-info-soft, #eff6ff)}@media(max-width:640px){.ngx-import-mapper__select{min-width:0;width:100%}}\n"] }]
|
|
10661
10895
|
}], propDecorators: { config: [{
|
|
10662
10896
|
type: Input,
|
|
10663
10897
|
args: [{ required: true }]
|
|
@@ -10702,13 +10936,23 @@ class ImportPreviewComponent {
|
|
|
10702
10936
|
/** The same rules the commit will apply; without them a rule-driven rejection is invisible. */
|
|
10703
10937
|
rules;
|
|
10704
10938
|
language = 'en';
|
|
10939
|
+
/** The decimal mark number cells use — the one the commit will read them with. */
|
|
10940
|
+
decimal = '.';
|
|
10705
10941
|
ui = inject(UiTextService);
|
|
10706
10942
|
/** The columns this plan fills, in the plan's own order. */
|
|
10707
10943
|
columns = signal([], ...(ngDevMode ? [{ debugName: "columns" }] : /* istanbul ignore next */ []));
|
|
10708
10944
|
grid = signal([], ...(ngDevMode ? [{ debugName: "grid" }] : /* istanbul ignore next */ []));
|
|
10709
10945
|
ngOnChanges() {
|
|
10710
|
-
|
|
10711
|
-
|
|
10946
|
+
// Sized by the plan, so a mapped sixth phone is shown rather than silently left out, and
|
|
10947
|
+
// read through the 2.2 alias, so an old ref shows the field it now means.
|
|
10948
|
+
const plan = upgradeLegacyRefs(this.plan, this.config);
|
|
10949
|
+
const byRef = new Map(deriveImportColumns(this.config, {
|
|
10950
|
+
lang: this.language,
|
|
10951
|
+
maxArrayRows: arrayBoundFor([], this.config, plan),
|
|
10952
|
+
includeReadonly: true,
|
|
10953
|
+
includeSystemDefault: true,
|
|
10954
|
+
}).columns.map(c => [c.ref, c]));
|
|
10955
|
+
const entries = (plan?.entries ?? []).filter(entry => byRef.has(entry.ref));
|
|
10712
10956
|
this.columns.set(entries.map(entry => byRef.get(entry.ref)));
|
|
10713
10957
|
// The same engine the commit runs, over the sample only. Row 0 of the sample is numbered
|
|
10714
10958
|
// 0 here rather than 2, because these row numbers index the preview, not the file.
|
|
@@ -10716,6 +10960,7 @@ class ImportPreviewComponent {
|
|
|
10716
10960
|
lang: this.language,
|
|
10717
10961
|
lookups: this.lookups,
|
|
10718
10962
|
rules: this.rules,
|
|
10963
|
+
decimal: this.decimal,
|
|
10719
10964
|
stamp: false,
|
|
10720
10965
|
firstRowNumber: 0,
|
|
10721
10966
|
});
|
|
@@ -10731,6 +10976,7 @@ class ImportPreviewComponent {
|
|
|
10731
10976
|
const outcome = coerceCell(field, raw, {
|
|
10732
10977
|
lang: this.language,
|
|
10733
10978
|
lookups: this.lookups,
|
|
10979
|
+
decimal: this.decimal,
|
|
10734
10980
|
});
|
|
10735
10981
|
if ('error' in outcome)
|
|
10736
10982
|
return { text: outcome.error, failed: true };
|
|
@@ -10741,8 +10987,8 @@ class ImportPreviewComponent {
|
|
|
10741
10987
|
}),
|
|
10742
10988
|
})));
|
|
10743
10989
|
}
|
|
10744
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
10745
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.
|
|
10990
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: ImportPreviewComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
10991
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.25", type: ImportPreviewComponent, isStandalone: true, selector: "ngx-import-preview", inputs: { config: "config", plan: "plan", rows: "rows", lookups: "lookups", rules: "rules", language: "language", decimal: "decimal" }, usesOnChanges: true, ngImport: i0, template: `
|
|
10746
10992
|
<section class="ngx-import-preview" data-testid="import-preview">
|
|
10747
10993
|
<h4 class="ngx-import-preview__heading">
|
|
10748
10994
|
{{ ui.text('importPreviewHeading', language, { count: rows.length }) }}
|
|
@@ -10780,7 +11026,7 @@ class ImportPreviewComponent {
|
|
|
10780
11026
|
</section>
|
|
10781
11027
|
`, isInline: true, styles: [".ngx-import-preview__heading{margin:0 0 8px;font-size:var(--ngx-font-size, 14px)}.ngx-import-preview__scroll{overflow-x:auto;border:1px solid var(--ngx-color-border, #e5e7eb);border-radius:var(--ngx-radius-sm, 6px)}.ngx-import-preview__table{width:100%;border-collapse:collapse;white-space:nowrap}.ngx-import-preview__table th,.ngx-import-preview__table td{text-align:left;padding:6px 10px;border-bottom:1px solid var(--ngx-color-border, #f3f4f6)}.ngx-import-preview__table thead th{background:var(--ngx-color-surface-alt, #f9fafb);color:var(--ngx-color-muted, #6b7280)}.ngx-import-preview__cell--error{color:var(--ngx-color-error, #b91c1c);background:var(--ngx-color-error-soft, #fef2f2)}.ngx-import-preview__row--rejected td{opacity:.65}.ngx-import-preview__reason td{white-space:normal;color:var(--ngx-color-error, #b91c1c);background:var(--ngx-color-error-soft, #fef2f2)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
10782
11028
|
}
|
|
10783
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
11029
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: ImportPreviewComponent, decorators: [{
|
|
10784
11030
|
type: Component,
|
|
10785
11031
|
args: [{ selector: 'ngx-import-preview', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [], template: `
|
|
10786
11032
|
<section class="ngx-import-preview" data-testid="import-preview">
|
|
@@ -10833,6 +11079,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.24", ngImpo
|
|
|
10833
11079
|
type: Input
|
|
10834
11080
|
}], language: [{
|
|
10835
11081
|
type: Input
|
|
11082
|
+
}], decimal: [{
|
|
11083
|
+
type: Input
|
|
10836
11084
|
}] } });
|
|
10837
11085
|
|
|
10838
11086
|
/**
|
|
@@ -10889,8 +11137,8 @@ class ImportTemplateComponent {
|
|
|
10889
11137
|
selectAll(selected) {
|
|
10890
11138
|
this.choices.update(choices => choices.map(choice => ({ ...choice, selected })));
|
|
10891
11139
|
}
|
|
10892
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
10893
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.
|
|
11140
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: ImportTemplateComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
11141
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.25", type: ImportTemplateComponent, isStandalone: true, selector: "ngx-import-template", inputs: { config: "config", language: "language" }, outputs: { download: "download" }, usesOnChanges: true, ngImport: i0, template: `
|
|
10894
11142
|
<section class="ngx-import-template" data-testid="import-template">
|
|
10895
11143
|
<h4 class="ngx-import-template__heading">{{ ui.text('importTemplateHeading', language) }}</h4>
|
|
10896
11144
|
<p class="ngx-import-template__explain">{{ ui.text('importTemplateExplain', language) }}</p>
|
|
@@ -10941,7 +11189,7 @@ class ImportTemplateComponent {
|
|
|
10941
11189
|
</section>
|
|
10942
11190
|
`, isInline: true, styles: [".ngx-import-template{padding:14px;border:1px solid var(--ngx-color-border, #e5e7eb);border-radius:var(--ngx-radius-sm, 6px);background:var(--ngx-color-surface-alt, #f9fafb)}.ngx-import-template__heading{margin:0 0 4px;font-size:var(--ngx-font-size, 14px)}.ngx-import-template__explain{margin:0 0 10px;color:var(--ngx-color-muted, #6b7280)}.ngx-import-template__bulk{display:flex;gap:8px;margin-bottom:10px}.ngx-import-template__list{list-style:none;margin:0 0 10px;padding:0;max-height:240px;overflow-y:auto;display:grid;gap:4px}.ngx-import-template__item{display:flex;flex-wrap:wrap;align-items:baseline;gap:8px}.ngx-import-template__item label{display:flex;align-items:center;gap:6px;cursor:pointer}.ngx-import-template__note{color:var(--ngx-color-muted, #6b7280);font-size:12px}.ngx-import-template__unsupported{margin:0 0 10px;color:var(--ngx-color-muted, #6b7280)}.ngx-import-template button{padding:6px 12px;border:1px solid var(--ngx-color-border, #d1d5db);border-radius:var(--ngx-radius-sm, 6px);background:var(--ngx-color-surface, #ffffff);color:inherit;font:inherit;cursor:pointer}.ngx-import-template button:disabled{opacity:.55;cursor:not-allowed}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
10943
11191
|
}
|
|
10944
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
11192
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: ImportTemplateComponent, decorators: [{
|
|
10945
11193
|
type: Component,
|
|
10946
11194
|
args: [{ selector: 'ngx-import-template', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [], template: `
|
|
10947
11195
|
<section class="ngx-import-template" data-testid="import-template">
|
|
@@ -11049,6 +11297,8 @@ class EntityImportComponent {
|
|
|
11049
11297
|
headers = signal([], ...(ngDevMode ? [{ debugName: "headers" }] : /* istanbul ignore next */ []));
|
|
11050
11298
|
sample = signal([], ...(ngDevMode ? [{ debugName: "sample" }] : /* istanbul ignore next */ []));
|
|
11051
11299
|
rowCount = signal(0, ...(ngDevMode ? [{ debugName: "rowCount" }] : /* istanbul ignore next */ []));
|
|
11300
|
+
/** The separator a delimited file turned out to use, so the user can see how it was read. */
|
|
11301
|
+
delimiter = signal(undefined, ...(ngDevMode ? [{ debugName: "delimiter" }] : /* istanbul ignore next */ []));
|
|
11052
11302
|
fileName = signal('', ...(ngDevMode ? [{ debugName: "fileName" }] : /* istanbul ignore next */ []));
|
|
11053
11303
|
plan = signal(null, ...(ngDevMode ? [{ debugName: "plan" }] : /* istanbul ignore next */ []));
|
|
11054
11304
|
result = signal(null, ...(ngDevMode ? [{ debugName: "result" }] : /* istanbul ignore next */ []));
|
|
@@ -11133,6 +11383,7 @@ class EntityImportComponent {
|
|
|
11133
11383
|
this.headers.set(preview.headers);
|
|
11134
11384
|
this.sample.set(preview.sample);
|
|
11135
11385
|
this.rowCount.set(preview.rowCount);
|
|
11386
|
+
this.delimiter.set(preview.delimiter);
|
|
11136
11387
|
this.plan.set(preview.suggestion);
|
|
11137
11388
|
this.step.set('map');
|
|
11138
11389
|
}
|
|
@@ -11216,19 +11467,37 @@ class EntityImportComponent {
|
|
|
11216
11467
|
failedRowCount(result) {
|
|
11217
11468
|
return result.failed ?? new Set(result.errors.map(error => error.row)).size;
|
|
11218
11469
|
}
|
|
11470
|
+
/** The decimal mark the commit will read numbers with, so the preview reads them the same. */
|
|
11471
|
+
decimal() {
|
|
11472
|
+
return decimalMarkFor(this.delimiter());
|
|
11473
|
+
}
|
|
11474
|
+
/** How the file's columns were split, in words — or nothing for a workbook. */
|
|
11475
|
+
delimiterText() {
|
|
11476
|
+
switch (this.delimiter()) {
|
|
11477
|
+
case ',':
|
|
11478
|
+
return this.ui.text('importDelimiterComma', this.language);
|
|
11479
|
+
case ';':
|
|
11480
|
+
return this.ui.text('importDelimiterSemicolon', this.language);
|
|
11481
|
+
case '\t':
|
|
11482
|
+
return this.ui.text('importDelimiterTab', this.language);
|
|
11483
|
+
default:
|
|
11484
|
+
return null;
|
|
11485
|
+
}
|
|
11486
|
+
}
|
|
11219
11487
|
restart() {
|
|
11220
11488
|
this.file = null;
|
|
11221
11489
|
this.fileName.set('');
|
|
11222
11490
|
this.headers.set([]);
|
|
11223
11491
|
this.sample.set([]);
|
|
11224
11492
|
this.rowCount.set(0);
|
|
11493
|
+
this.delimiter.set(undefined);
|
|
11225
11494
|
this.plan.set(null);
|
|
11226
11495
|
this.result.set(null);
|
|
11227
11496
|
this.problem.set(null);
|
|
11228
11497
|
this.step.set('upload');
|
|
11229
11498
|
}
|
|
11230
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.
|
|
11231
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.
|
|
11499
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: EntityImportComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
11500
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.25", type: EntityImportComponent, isStandalone: true, selector: "ngx-entity-import", inputs: { config: "config", rules: "rules", lookups: "lookups", language: "language", templateFormat: "templateFormat" }, outputs: { importComplete: "importComplete", templateReady: "templateReady" }, ngImport: i0, template: `
|
|
11232
11501
|
<div class="ngx-import" data-testid="entity-import">
|
|
11233
11502
|
<ol class="ngx-import__steps" data-testid="import-steps">
|
|
11234
11503
|
@for (name of steps; track name) {
|
|
@@ -11284,6 +11553,9 @@ class EntityImportComponent {
|
|
|
11284
11553
|
}
|
|
11285
11554
|
|
|
11286
11555
|
@case ('review') {
|
|
11556
|
+
@if (delimiterText(); as separator) {
|
|
11557
|
+
<p class="ngx-entity-import__delimiter" data-testid="import-delimiter">{{ separator }}</p>
|
|
11558
|
+
}
|
|
11287
11559
|
@if (plan(); as current) {
|
|
11288
11560
|
<ngx-import-preview
|
|
11289
11561
|
[config]="config"
|
|
@@ -11292,6 +11564,7 @@ class EntityImportComponent {
|
|
|
11292
11564
|
[lookups]="resolvedLookups()"
|
|
11293
11565
|
[rules]="rules"
|
|
11294
11566
|
[language]="language"
|
|
11567
|
+
[decimal]="decimal()"
|
|
11295
11568
|
/>
|
|
11296
11569
|
}
|
|
11297
11570
|
<button
|
|
@@ -11344,9 +11617,9 @@ class EntityImportComponent {
|
|
|
11344
11617
|
}
|
|
11345
11618
|
}
|
|
11346
11619
|
</div>
|
|
11347
|
-
`, isInline: true, styles: [".ngx-import{display:flex;flex-direction:column;gap:var(--ngx-gap, 16px);color:var(--ngx-color-text, #1f2937);font-size:var(--ngx-font-size, 14px)}.ngx-import__steps{display:flex;flex-wrap:wrap;gap:8px;list-style:none;margin:0;padding:0}.ngx-import__step{padding:4px 12px;border:1px solid var(--ngx-color-border, #e5e7eb);border-radius:var(--ngx-radius-sm, 6px);color:var(--ngx-color-muted, #6b7280);background:var(--ngx-color-surface-alt, #f9fafb)}.ngx-import__step--current{color:var(--ngx-color-accent, #4f46e5);border-color:var(--ngx-color-accent, #4f46e5);background:var(--ngx-color-accent-soft, #e0e7ff);font-weight:600}.ngx-import__problem{margin:0;padding:10px 12px;border:1px solid var(--ngx-color-border, #fecaca);border-radius:var(--ngx-radius-sm, 6px);background:var(--ngx-color-error-soft, #fef2f2);color:var(--ngx-color-error, #b91c1c);overflow-wrap:anywhere}.ngx-import__file{display:flex;flex-direction:column;gap:6px;padding:14px;border:1px dashed var(--ngx-color-border, #d1d5db);border-radius:var(--ngx-radius-sm, 6px);background:var(--ngx-color-surface, #ffffff)}.ngx-import button{align-self:flex-start;padding:8px 16px;border:1px solid var(--ngx-color-border, #d1d5db);border-radius:var(--ngx-radius-sm, 6px);background:var(--ngx-color-surface, #ffffff);color:inherit;font:inherit;cursor:pointer}.ngx-import button:hover:not(:disabled){border-color:var(--ngx-color-accent, #4f46e5);background:var(--ngx-color-accent-soft, #e0e7ff)}.ngx-import button:disabled{opacity:.55;cursor:not-allowed}\n"], dependencies: [{ kind: "component", type: ImportTemplateComponent, selector: "ngx-import-template", inputs: ["config", "language"], outputs: ["download"] }, { kind: "component", type: ImportMapperComponent, selector: "ngx-import-mapper", inputs: ["config", "headers", "plan", "language"], outputs: ["planChange"] }, { kind: "component", type: ImportPreviewComponent, selector: "ngx-import-preview", inputs: ["config", "plan", "rows", "lookups", "rules", "language"] }, { kind: "component", type: ImportErrorsComponent, selector: "ngx-import-errors", inputs: ["errors", "language"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
11620
|
+
`, isInline: true, styles: [".ngx-import{display:flex;flex-direction:column;gap:var(--ngx-gap, 16px);color:var(--ngx-color-text, #1f2937);font-size:var(--ngx-font-size, 14px)}.ngx-import__steps{display:flex;flex-wrap:wrap;gap:8px;list-style:none;margin:0;padding:0}.ngx-import__step{padding:4px 12px;border:1px solid var(--ngx-color-border, #e5e7eb);border-radius:var(--ngx-radius-sm, 6px);color:var(--ngx-color-muted, #6b7280);background:var(--ngx-color-surface-alt, #f9fafb)}.ngx-import__step--current{color:var(--ngx-color-accent, #4f46e5);border-color:var(--ngx-color-accent, #4f46e5);background:var(--ngx-color-accent-soft, #e0e7ff);font-weight:600}.ngx-import__problem{margin:0;padding:10px 12px;border:1px solid var(--ngx-color-border, #fecaca);border-radius:var(--ngx-radius-sm, 6px);background:var(--ngx-color-error-soft, #fef2f2);color:var(--ngx-color-error, #b91c1c);overflow-wrap:anywhere}.ngx-import__file{display:flex;flex-direction:column;gap:6px;padding:14px;border:1px dashed var(--ngx-color-border, #d1d5db);border-radius:var(--ngx-radius-sm, 6px);background:var(--ngx-color-surface, #ffffff)}.ngx-import button{align-self:flex-start;padding:8px 16px;border:1px solid var(--ngx-color-border, #d1d5db);border-radius:var(--ngx-radius-sm, 6px);background:var(--ngx-color-surface, #ffffff);color:inherit;font:inherit;cursor:pointer}.ngx-import button:hover:not(:disabled){border-color:var(--ngx-color-accent, #4f46e5);background:var(--ngx-color-accent-soft, #e0e7ff)}.ngx-import button:disabled{opacity:.55;cursor:not-allowed}\n"], dependencies: [{ kind: "component", type: ImportTemplateComponent, selector: "ngx-import-template", inputs: ["config", "language"], outputs: ["download"] }, { kind: "component", type: ImportMapperComponent, selector: "ngx-import-mapper", inputs: ["config", "headers", "plan", "language"], outputs: ["planChange"] }, { kind: "component", type: ImportPreviewComponent, selector: "ngx-import-preview", inputs: ["config", "plan", "rows", "lookups", "rules", "language", "decimal"] }, { kind: "component", type: ImportErrorsComponent, selector: "ngx-import-errors", inputs: ["errors", "language"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
11348
11621
|
}
|
|
11349
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.
|
|
11622
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.25", ngImport: i0, type: EntityImportComponent, decorators: [{
|
|
11350
11623
|
type: Component,
|
|
11351
11624
|
args: [{ selector: 'ngx-entity-import', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, imports: [
|
|
11352
11625
|
ImportTemplateComponent,
|
|
@@ -11409,6 +11682,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.24", ngImpo
|
|
|
11409
11682
|
}
|
|
11410
11683
|
|
|
11411
11684
|
@case ('review') {
|
|
11685
|
+
@if (delimiterText(); as separator) {
|
|
11686
|
+
<p class="ngx-entity-import__delimiter" data-testid="import-delimiter">{{ separator }}</p>
|
|
11687
|
+
}
|
|
11412
11688
|
@if (plan(); as current) {
|
|
11413
11689
|
<ngx-import-preview
|
|
11414
11690
|
[config]="config"
|
|
@@ -11417,6 +11693,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.24", ngImpo
|
|
|
11417
11693
|
[lookups]="resolvedLookups()"
|
|
11418
11694
|
[rules]="rules"
|
|
11419
11695
|
[language]="language"
|
|
11696
|
+
[decimal]="decimal()"
|
|
11420
11697
|
/>
|
|
11421
11698
|
}
|
|
11422
11699
|
<button
|