@praxisui/dynamic-fields 8.0.0-beta.24 → 8.0.0-beta.25
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/fesm2022/praxisui-dynamic-fields.mjs +259 -178
- package/index.d.ts +6 -0
- package/package.json +3 -3
|
@@ -752,6 +752,161 @@ function resolvePraxisDynamicFieldsText(value, fallback) {
|
|
|
752
752
|
return { text: fallback ?? '' };
|
|
753
753
|
}
|
|
754
754
|
|
|
755
|
+
function resolveInlineDisplayMask(metadata, currentValue) {
|
|
756
|
+
const explicitMask = stringValue(metadata['displayMask']) ||
|
|
757
|
+
stringValue(metadata['mask']) ||
|
|
758
|
+
stringValue(metadata['inputMask']) ||
|
|
759
|
+
maskFromFormat(metadata['format']);
|
|
760
|
+
if (explicitMask) {
|
|
761
|
+
if (hasPhoneSignal(metadata, buildComparableSignals(metadata))) {
|
|
762
|
+
const digits = digitsOnly(currentValue);
|
|
763
|
+
if (digits.startsWith('55') && digits.length > 11 && explicitMask.includes('+55')) {
|
|
764
|
+
return {
|
|
765
|
+
mask: resolveBrazilianPhoneMask(currentValue),
|
|
766
|
+
rawMode: 'digits',
|
|
767
|
+
};
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
return {
|
|
771
|
+
mask: explicitMask,
|
|
772
|
+
rawMode: /[Xx]/.test(explicitMask) ? 'alphanumeric' : 'digits',
|
|
773
|
+
};
|
|
774
|
+
}
|
|
775
|
+
const documentType = normalizeComparableToken(metadata['documentType'] ??
|
|
776
|
+
metadata['documentKind'] ??
|
|
777
|
+
metadata['identifierType'] ??
|
|
778
|
+
metadata['identifierKind']);
|
|
779
|
+
if (documentType === 'cpf') {
|
|
780
|
+
return { mask: '000.000.000-00', rawMode: 'digits' };
|
|
781
|
+
}
|
|
782
|
+
if (documentType === 'cnpj') {
|
|
783
|
+
return { mask: '00.000.000/0000-00', rawMode: 'digits' };
|
|
784
|
+
}
|
|
785
|
+
const signals = buildComparableSignals(metadata);
|
|
786
|
+
if (hasSignal(signals, 'cpfcnpj')) {
|
|
787
|
+
const digits = digitsOnly(currentValue);
|
|
788
|
+
return {
|
|
789
|
+
mask: digits.length > 11 ? '00.000.000/0000-00' : '000.000.000-00',
|
|
790
|
+
rawMode: 'digits',
|
|
791
|
+
};
|
|
792
|
+
}
|
|
793
|
+
if (hasSignal(signals, 'cpf')) {
|
|
794
|
+
return { mask: '000.000.000-00', rawMode: 'digits' };
|
|
795
|
+
}
|
|
796
|
+
if (hasSignal(signals, 'cnpj')) {
|
|
797
|
+
return { mask: '00.000.000/0000-00', rawMode: 'digits' };
|
|
798
|
+
}
|
|
799
|
+
if (hasPhoneSignal(metadata, signals)) {
|
|
800
|
+
return {
|
|
801
|
+
mask: resolveBrazilianPhoneMask(currentValue),
|
|
802
|
+
rawMode: 'digits',
|
|
803
|
+
};
|
|
804
|
+
}
|
|
805
|
+
if (hasPostalCodeSignal(signals)) {
|
|
806
|
+
return { mask: '00000-000', rawMode: 'digits' };
|
|
807
|
+
}
|
|
808
|
+
return null;
|
|
809
|
+
}
|
|
810
|
+
function applyInlineDisplayMask(value, mask) {
|
|
811
|
+
if (!value) {
|
|
812
|
+
return '';
|
|
813
|
+
}
|
|
814
|
+
let valueIndex = 0;
|
|
815
|
+
let output = '';
|
|
816
|
+
for (const token of mask.mask) {
|
|
817
|
+
if (valueIndex >= value.length) {
|
|
818
|
+
break;
|
|
819
|
+
}
|
|
820
|
+
if (isMaskPlaceholder(token)) {
|
|
821
|
+
output += value[valueIndex++] ?? '';
|
|
822
|
+
}
|
|
823
|
+
else {
|
|
824
|
+
output += token;
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
return output;
|
|
828
|
+
}
|
|
829
|
+
function unmaskInlineDisplayValue(value, mask) {
|
|
830
|
+
const text = String(value ?? '');
|
|
831
|
+
return mask.rawMode === 'alphanumeric'
|
|
832
|
+
? text.replace(/[^a-zA-Z0-9]/g, '')
|
|
833
|
+
: digitsOnly(text);
|
|
834
|
+
}
|
|
835
|
+
function resolveBrazilianPhoneMask(value) {
|
|
836
|
+
const digits = digitsOnly(value);
|
|
837
|
+
if (digits.startsWith('55') && digits.length > 11) {
|
|
838
|
+
return '+00 00 00000-0000';
|
|
839
|
+
}
|
|
840
|
+
return digits.length <= 10 ? '(00) 0000-0000' : '(00) 00000-0000';
|
|
841
|
+
}
|
|
842
|
+
function hasPhoneSignal(metadata, signals) {
|
|
843
|
+
const phoneFormat = normalizeComparableToken(metadata['phoneFormat']);
|
|
844
|
+
const defaultCountry = normalizeComparableToken(metadata['defaultCountry']);
|
|
845
|
+
return (phoneFormat === 'national' ||
|
|
846
|
+
phoneFormat === 'international' ||
|
|
847
|
+
phoneFormat === 'e164' ||
|
|
848
|
+
defaultCountry === 'br' ||
|
|
849
|
+
hasSignal(signals, 'phone') ||
|
|
850
|
+
hasSignal(signals, 'telefone') ||
|
|
851
|
+
hasSignal(signals, 'celular') ||
|
|
852
|
+
hasSignal(signals, 'mobile') ||
|
|
853
|
+
hasSignal(signals, 'whatsapp') ||
|
|
854
|
+
hasSignal(signals, 'tel'));
|
|
855
|
+
}
|
|
856
|
+
function hasPostalCodeSignal(signals) {
|
|
857
|
+
return (hasSignal(signals, 'cep') ||
|
|
858
|
+
hasSignal(signals, 'postalcode') ||
|
|
859
|
+
hasSignal(signals, 'zipcode') ||
|
|
860
|
+
hasSignal(signals, 'zip'));
|
|
861
|
+
}
|
|
862
|
+
function buildComparableSignals(metadata) {
|
|
863
|
+
return [
|
|
864
|
+
metadata['name'],
|
|
865
|
+
metadata['id'],
|
|
866
|
+
metadata['key'],
|
|
867
|
+
metadata['label'],
|
|
868
|
+
metadata['placeholder'],
|
|
869
|
+
metadata['controlType'],
|
|
870
|
+
metadata['type'],
|
|
871
|
+
metadata['dataType'],
|
|
872
|
+
metadata['format'],
|
|
873
|
+
metadata['mask'],
|
|
874
|
+
metadata['inputMask'],
|
|
875
|
+
]
|
|
876
|
+
.map(normalizeComparableToken)
|
|
877
|
+
.filter(Boolean)
|
|
878
|
+
.join('|');
|
|
879
|
+
}
|
|
880
|
+
function hasSignal(signals, token) {
|
|
881
|
+
return signals.split('|').includes(token) || signals.includes(token);
|
|
882
|
+
}
|
|
883
|
+
function maskFromFormat(value) {
|
|
884
|
+
const format = stringValue(value);
|
|
885
|
+
if (!format) {
|
|
886
|
+
return null;
|
|
887
|
+
}
|
|
888
|
+
const hasMaskTokens = /[0#9Xx]/.test(format);
|
|
889
|
+
const hasInvalidLetters = /[A-WYZawyz]/.test(format);
|
|
890
|
+
return hasMaskTokens && !hasInvalidLetters ? format : null;
|
|
891
|
+
}
|
|
892
|
+
function stringValue(value) {
|
|
893
|
+
return typeof value === 'string' && value.trim() ? value.trim() : null;
|
|
894
|
+
}
|
|
895
|
+
function normalizeComparableToken(value) {
|
|
896
|
+
return String(value ?? '')
|
|
897
|
+
.trim()
|
|
898
|
+
.toLowerCase()
|
|
899
|
+
.normalize('NFD')
|
|
900
|
+
.replace(/[\u0300-\u036f]/g, '')
|
|
901
|
+
.replace(/[\s_-]+/g, '');
|
|
902
|
+
}
|
|
903
|
+
function digitsOnly(value) {
|
|
904
|
+
return String(value ?? '').replace(/\D/g, '');
|
|
905
|
+
}
|
|
906
|
+
function isMaskPlaceholder(token) {
|
|
907
|
+
return token === '0' || token === '9' || token === '#' || token === 'X' || token === 'x';
|
|
908
|
+
}
|
|
909
|
+
|
|
755
910
|
/**
|
|
756
911
|
* @fileoverview Simple base component for input fields with basic ControlValueAccessor functionality
|
|
757
912
|
*
|
|
@@ -1375,6 +1530,7 @@ class SimpleBaseInputComponent {
|
|
|
1375
1530
|
this.syncInProgress = false;
|
|
1376
1531
|
}
|
|
1377
1532
|
}
|
|
1533
|
+
queueMicrotask(() => this.applyNativeDisplayMask());
|
|
1378
1534
|
}
|
|
1379
1535
|
registerOnChange(fn) {
|
|
1380
1536
|
this.onChange = fn;
|
|
@@ -1405,6 +1561,9 @@ class SimpleBaseInputComponent {
|
|
|
1405
1561
|
// =============================================================================
|
|
1406
1562
|
handleInput(event) {
|
|
1407
1563
|
const input = event.target;
|
|
1564
|
+
if (this.handleMaskedNativeInput(input)) {
|
|
1565
|
+
return;
|
|
1566
|
+
}
|
|
1408
1567
|
const value = input.value;
|
|
1409
1568
|
if (!this.syncInProgress) {
|
|
1410
1569
|
this.syncInProgress = true;
|
|
@@ -1718,6 +1877,7 @@ class SimpleBaseInputComponent {
|
|
|
1718
1877
|
this.nativeElement = el;
|
|
1719
1878
|
this.attachNativeEventHandlers();
|
|
1720
1879
|
this.applyNativeAttributes();
|
|
1880
|
+
this.applyNativeDisplayMask();
|
|
1721
1881
|
this.log('debug', 'registerInputElement: element attached and attributes applied');
|
|
1722
1882
|
}
|
|
1723
1883
|
/** Aplica atributos e ARIA com base no metadata */
|
|
@@ -1903,6 +2063,7 @@ class SimpleBaseInputComponent {
|
|
|
1903
2063
|
.subscribe((value) => {
|
|
1904
2064
|
this.fieldState.update((state) => ({ ...state, value }));
|
|
1905
2065
|
this.syncComponentStateFromControl(control);
|
|
2066
|
+
queueMicrotask(() => this.applyNativeDisplayMask(value));
|
|
1906
2067
|
});
|
|
1907
2068
|
control.statusChanges
|
|
1908
2069
|
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
@@ -1942,6 +2103,59 @@ class SimpleBaseInputComponent {
|
|
|
1942
2103
|
disabled: !!control.disabled,
|
|
1943
2104
|
}));
|
|
1944
2105
|
}
|
|
2106
|
+
handleMaskedNativeInput(input) {
|
|
2107
|
+
if (!input) {
|
|
2108
|
+
return false;
|
|
2109
|
+
}
|
|
2110
|
+
const mask = this.resolveNativeDisplayMask(input.value);
|
|
2111
|
+
if (!mask) {
|
|
2112
|
+
return false;
|
|
2113
|
+
}
|
|
2114
|
+
const rawValue = unmaskInlineDisplayValue(input.value, mask);
|
|
2115
|
+
const displayValue = applyInlineDisplayMask(rawValue, mask);
|
|
2116
|
+
if (!this.syncInProgress) {
|
|
2117
|
+
this.syncInProgress = true;
|
|
2118
|
+
try {
|
|
2119
|
+
const control = this.control();
|
|
2120
|
+
control.setValue(rawValue, { emitEvent: false });
|
|
2121
|
+
this.onChange(rawValue);
|
|
2122
|
+
this.valueChange.emit(rawValue);
|
|
2123
|
+
this.fieldState.update((state) => ({ ...state, value: rawValue }));
|
|
2124
|
+
this.markAsDirty();
|
|
2125
|
+
this.applyErrorDebounceOnChange();
|
|
2126
|
+
}
|
|
2127
|
+
finally {
|
|
2128
|
+
this.syncInProgress = false;
|
|
2129
|
+
}
|
|
2130
|
+
}
|
|
2131
|
+
queueMicrotask(() => {
|
|
2132
|
+
if (input.value !== displayValue) {
|
|
2133
|
+
input.value = displayValue;
|
|
2134
|
+
}
|
|
2135
|
+
});
|
|
2136
|
+
return true;
|
|
2137
|
+
}
|
|
2138
|
+
applyNativeDisplayMask(value = this.control().value) {
|
|
2139
|
+
const input = this.nativeElement;
|
|
2140
|
+
if (!input || input.tagName.toLowerCase() !== 'input') {
|
|
2141
|
+
return;
|
|
2142
|
+
}
|
|
2143
|
+
const mask = this.resolveNativeDisplayMask(value);
|
|
2144
|
+
if (!mask) {
|
|
2145
|
+
return;
|
|
2146
|
+
}
|
|
2147
|
+
const displayValue = applyInlineDisplayMask(String(value ?? ''), mask);
|
|
2148
|
+
if (input.value !== displayValue) {
|
|
2149
|
+
input.value = displayValue;
|
|
2150
|
+
}
|
|
2151
|
+
}
|
|
2152
|
+
resolveNativeDisplayMask(value) {
|
|
2153
|
+
const metadata = this.metadata();
|
|
2154
|
+
if (!metadata) {
|
|
2155
|
+
return null;
|
|
2156
|
+
}
|
|
2157
|
+
return resolveInlineDisplayMask(metadata, value);
|
|
2158
|
+
}
|
|
1945
2159
|
// Atualiza tooltip/atributos de erro quando erros mudam e quando inline errors estiverem desabilitados
|
|
1946
2160
|
refreshErrorTooltip() {
|
|
1947
2161
|
if (!this.nativeElement)
|
|
@@ -2947,7 +3161,9 @@ class SimpleBaseSelectComponent extends SimpleBaseInputComponent {
|
|
|
2947
3161
|
this.emptyOptionText.set(isMultiple ? null : (metadata.emptyOptionText ?? fallbackEmptyOption));
|
|
2948
3162
|
const optionSource = metadata.optionSource ?? null;
|
|
2949
3163
|
this.optionSource.set(optionSource);
|
|
2950
|
-
this.useOptionsFilterEndpoint.set(this.isOptionsFilterEndpoint(metadata.endpoint)
|
|
3164
|
+
this.useOptionsFilterEndpoint.set(this.isOptionsFilterEndpoint(metadata.endpoint) ||
|
|
3165
|
+
this.isOptionsFilterEndpoint(metadata.optionsEndpoint) ||
|
|
3166
|
+
String(metadata.optionsEndpoint ?? '').trim().toLowerCase() === 'filter');
|
|
2951
3167
|
const path = optionSource?.resourcePath ?? metadata.resourcePath ?? metadata.endpoint;
|
|
2952
3168
|
if (path) {
|
|
2953
3169
|
this.resourcePath.set(path);
|
|
@@ -3038,8 +3254,10 @@ class SimpleBaseSelectComponent extends SimpleBaseInputComponent {
|
|
|
3038
3254
|
const meta = this.metadata();
|
|
3039
3255
|
if (!meta)
|
|
3040
3256
|
return;
|
|
3041
|
-
|
|
3042
|
-
|
|
3257
|
+
this.matSelect.compareWith =
|
|
3258
|
+
typeof meta.compareWith === 'function'
|
|
3259
|
+
? meta.compareWith
|
|
3260
|
+
: (left, right) => this.areOptionValuesEqual(left, right);
|
|
3043
3261
|
// MatSelect does not support a displayWith property; formatting should
|
|
3044
3262
|
// be handled via option labels or mat-select-trigger in templates.
|
|
3045
3263
|
const panelClass = this.selectPanelClass();
|
|
@@ -3378,7 +3596,16 @@ class SimpleBaseSelectComponent extends SimpleBaseInputComponent {
|
|
|
3378
3596
|
return currentCount >= max;
|
|
3379
3597
|
}
|
|
3380
3598
|
areOptionValuesEqual(left, right) {
|
|
3381
|
-
return left === right;
|
|
3599
|
+
return this.normalizeComparableOptionValue(left) === this.normalizeComparableOptionValue(right);
|
|
3600
|
+
}
|
|
3601
|
+
normalizeComparableOptionValue(value) {
|
|
3602
|
+
if (value && typeof value === 'object' && 'id' in value) {
|
|
3603
|
+
return this.normalizeComparableOptionValue(value.id);
|
|
3604
|
+
}
|
|
3605
|
+
if (typeof value === 'string' || typeof value === 'number') {
|
|
3606
|
+
return String(value).trim();
|
|
3607
|
+
}
|
|
3608
|
+
return value;
|
|
3382
3609
|
}
|
|
3383
3610
|
/**
|
|
3384
3611
|
* Toggles selection of all options when `selectAll` is enabled in multiple
|
|
@@ -3476,6 +3703,10 @@ class SimpleBaseSelectComponent extends SimpleBaseInputComponent {
|
|
|
3476
3703
|
if (left === right) {
|
|
3477
3704
|
return true;
|
|
3478
3705
|
}
|
|
3706
|
+
if (this.normalizeComparableOptionValue(left) ===
|
|
3707
|
+
this.normalizeComparableOptionValue(right)) {
|
|
3708
|
+
return true;
|
|
3709
|
+
}
|
|
3479
3710
|
try {
|
|
3480
3711
|
return JSON.stringify(left) === JSON.stringify(right);
|
|
3481
3712
|
}
|
|
@@ -11444,10 +11675,10 @@ class MaterialAsyncSelectComponent extends SimpleBaseSelectComponent {
|
|
|
11444
11675
|
request$
|
|
11445
11676
|
.pipe(takeUntilDestroyed(this.destroyRef))
|
|
11446
11677
|
.subscribe((opts) => {
|
|
11447
|
-
const returned = new Set(opts.map((o) => o.id));
|
|
11678
|
+
const returned = new Set(opts.map((o) => this.normalizeComparableOptionValue(o.id)));
|
|
11448
11679
|
opts.forEach((o) => this.store.upsertById(o));
|
|
11449
11680
|
ids
|
|
11450
|
-
.filter((id) => !returned.has(id))
|
|
11681
|
+
.filter((id) => !returned.has(this.normalizeComparableOptionValue(id)))
|
|
11451
11682
|
.forEach((id) => this.store.upsertById({ id, label: this.missingOptionLabel(id) }, true));
|
|
11452
11683
|
this.store.ensureVisible(ids);
|
|
11453
11684
|
this.refreshOptions();
|
|
@@ -11748,7 +11979,8 @@ class MaterialAsyncSelectComponent extends SimpleBaseSelectComponent {
|
|
|
11748
11979
|
return option.extra?.['selectable'] === false;
|
|
11749
11980
|
}
|
|
11750
11981
|
areOptionValuesEqual(left, right) {
|
|
11751
|
-
return this.normalizeRemoteOptionValue(left) ===
|
|
11982
|
+
return (this.normalizeComparableOptionValue(this.normalizeRemoteOptionValue(left)) ===
|
|
11983
|
+
this.normalizeComparableOptionValue(this.normalizeRemoteOptionValue(right)));
|
|
11752
11984
|
}
|
|
11753
11985
|
normalizeRemoteOptionValue(value) {
|
|
11754
11986
|
if (value && typeof value === 'object' && 'id' in value) {
|
|
@@ -16039,6 +16271,8 @@ class MaterialSelectComponent extends SimpleBaseSelectComponent {
|
|
|
16039
16271
|
this.devWarnSelectAliases(matMetadata, 'MaterialSelectComponent');
|
|
16040
16272
|
const source = matMetadata.selectOptions ?? this.optionsFromLegacy(matMetadata);
|
|
16041
16273
|
const mappedOptions = source?.map((option) => this.normalizeOption(option));
|
|
16274
|
+
const optionLabelKey = matMetadata.optionLabelKey ?? matMetadata.displayField;
|
|
16275
|
+
const optionValueKey = matMetadata.optionValueKey ?? matMetadata.valueField;
|
|
16042
16276
|
super.setSelectMetadata({
|
|
16043
16277
|
...matMetadata,
|
|
16044
16278
|
options: mappedOptions,
|
|
@@ -16046,8 +16280,8 @@ class MaterialSelectComponent extends SimpleBaseSelectComponent {
|
|
|
16046
16280
|
searchable: matMetadata.searchable,
|
|
16047
16281
|
resourcePath: matMetadata.resourcePath,
|
|
16048
16282
|
filterCriteria: matMetadata.filterCriteria,
|
|
16049
|
-
optionLabelKey
|
|
16050
|
-
optionValueKey
|
|
16283
|
+
optionLabelKey,
|
|
16284
|
+
optionValueKey,
|
|
16051
16285
|
});
|
|
16052
16286
|
}
|
|
16053
16287
|
ngOnInit() {
|
|
@@ -16071,9 +16305,10 @@ class MaterialSelectComponent extends SimpleBaseSelectComponent {
|
|
|
16071
16305
|
}
|
|
16072
16306
|
normalizeOption(option) {
|
|
16073
16307
|
const label = option['label'] ?? option['text'];
|
|
16308
|
+
const value = option['value'] ?? option['id'];
|
|
16074
16309
|
return {
|
|
16075
16310
|
label: typeof label === 'string' ? label : String(label ?? ''),
|
|
16076
|
-
value
|
|
16311
|
+
value,
|
|
16077
16312
|
disabled: option['disabled'] === true ? true : undefined,
|
|
16078
16313
|
};
|
|
16079
16314
|
}
|
|
@@ -17226,6 +17461,9 @@ class PhoneInputComponent extends SimpleBaseInputComponent {
|
|
|
17226
17461
|
super.registerInputElement(el);
|
|
17227
17462
|
this.applyPhoneDisplayValue();
|
|
17228
17463
|
}
|
|
17464
|
+
applyNativeDisplayMask() {
|
|
17465
|
+
this.applyPhoneDisplayValue();
|
|
17466
|
+
}
|
|
17229
17467
|
handleInput(event) {
|
|
17230
17468
|
if (!this.isPhoneRuntimeEnabled()) {
|
|
17231
17469
|
super.handleInput(event);
|
|
@@ -20456,6 +20694,11 @@ class MaterialCurrencyComponent extends SimpleBaseInputComponent {
|
|
|
20456
20694
|
this.setCaretToEnd();
|
|
20457
20695
|
}
|
|
20458
20696
|
}
|
|
20697
|
+
applyNativeDisplayMask() {
|
|
20698
|
+
if (this.inputRef?.nativeElement) {
|
|
20699
|
+
this.formatDisplayValue();
|
|
20700
|
+
}
|
|
20701
|
+
}
|
|
20459
20702
|
async validateField() {
|
|
20460
20703
|
const errors = await super.validateField();
|
|
20461
20704
|
this.validationChange.emit(errors);
|
|
@@ -20733,14 +20976,11 @@ class MaterialCurrencyComponent extends SimpleBaseInputComponent {
|
|
|
20733
20976
|
</mat-hint>
|
|
20734
20977
|
}
|
|
20735
20978
|
</mat-form-field>
|
|
20736
|
-
`, isInline: true, dependencies: [{ kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: i2.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i2.MatPrefix, selector: "[matPrefix], [matIconPrefix], [matTextPrefix]", inputs: ["matTextPrefix"] }, { kind: "directive", type: i2.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3$1.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i5.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.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$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }] });
|
|
20979
|
+
`, isInline: true, styles: [":host ::ng-deep .pdx-material-currency .mat-mdc-form-field-icon-prefix,:host ::ng-deep .pdx-material-currency .mat-mdc-form-field-text-prefix,:host ::ng-deep .pdx-material-currency .mat-mdc-form-field-icon-suffix,:host ::ng-deep .pdx-material-currency .mat-mdc-form-field-text-suffix{align-self:center;display:inline-flex;align-items:center}.currency-symbol{display:inline-flex;align-items:center;line-height:1;white-space:nowrap}\n"], dependencies: [{ kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i1$2.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i2.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i2.MatLabel, selector: "mat-label" }, { kind: "directive", type: i2.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: i2.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: i2.MatPrefix, selector: "[matPrefix], [matIconPrefix], [matTextPrefix]", inputs: ["matTextPrefix"] }, { kind: "directive", type: i2.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i3$1.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i4.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i5.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.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$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }] });
|
|
20737
20980
|
}
|
|
20738
20981
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: MaterialCurrencyComponent, decorators: [{
|
|
20739
20982
|
type: Component,
|
|
20740
|
-
args: [{
|
|
20741
|
-
selector: 'pdx-material-currency',
|
|
20742
|
-
standalone: true,
|
|
20743
|
-
template: `
|
|
20983
|
+
args: [{ selector: 'pdx-material-currency', standalone: true, template: `
|
|
20744
20984
|
<mat-form-field
|
|
20745
20985
|
[appearance]="materialAppearance()"
|
|
20746
20986
|
[color]="materialColor()"
|
|
@@ -20830,8 +21070,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
20830
21070
|
</mat-hint>
|
|
20831
21071
|
}
|
|
20832
21072
|
</mat-form-field>
|
|
20833
|
-
`,
|
|
20834
|
-
imports: [
|
|
21073
|
+
`, imports: [
|
|
20835
21074
|
MatButtonModule,
|
|
20836
21075
|
CommonModule,
|
|
20837
21076
|
MatFormFieldModule,
|
|
@@ -20840,16 +21079,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
20840
21079
|
MatTooltipModule,
|
|
20841
21080
|
PraxisIconDirective,
|
|
20842
21081
|
ReactiveFormsModule,
|
|
20843
|
-
],
|
|
20844
|
-
providers: [
|
|
21082
|
+
], providers: [
|
|
20845
21083
|
CurrencyPipe,
|
|
20846
21084
|
{
|
|
20847
21085
|
provide: NG_VALUE_ACCESSOR,
|
|
20848
21086
|
useExisting: forwardRef(() => MaterialCurrencyComponent),
|
|
20849
21087
|
multi: true,
|
|
20850
21088
|
},
|
|
20851
|
-
],
|
|
20852
|
-
host: {
|
|
21089
|
+
], host: {
|
|
20853
21090
|
'[class]': 'componentCssClasses()',
|
|
20854
21091
|
'[class.praxis-disabled]': 'disabledMode',
|
|
20855
21092
|
'[style.display]': 'visible ? null : "none"',
|
|
@@ -20857,8 +21094,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
20857
21094
|
'[attr.data-field-type]': '"currency"',
|
|
20858
21095
|
'[attr.data-field-name]': 'metadata()?.name',
|
|
20859
21096
|
'[attr.data-component-id]': 'componentId()',
|
|
20860
|
-
},
|
|
20861
|
-
}]
|
|
21097
|
+
}, styles: [":host ::ng-deep .pdx-material-currency .mat-mdc-form-field-icon-prefix,:host ::ng-deep .pdx-material-currency .mat-mdc-form-field-text-prefix,:host ::ng-deep .pdx-material-currency .mat-mdc-form-field-icon-suffix,:host ::ng-deep .pdx-material-currency .mat-mdc-form-field-text-suffix{align-self:center;display:inline-flex;align-items:center}.currency-symbol{display:inline-flex;align-items:center;line-height:1;white-space:nowrap}\n"] }]
|
|
20862
21098
|
}], propDecorators: { validationChange: [{ type: i0.Output, args: ["validationChange"] }], inputRef: [{
|
|
20863
21099
|
type: ViewChild,
|
|
20864
21100
|
args: ['currencyInput', { static: true }]
|
|
@@ -25369,161 +25605,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
25369
25605
|
args: ['window:resize']
|
|
25370
25606
|
}] } });
|
|
25371
25607
|
|
|
25372
|
-
function resolveInlineDisplayMask(metadata, currentValue) {
|
|
25373
|
-
const explicitMask = stringValue(metadata['displayMask']) ||
|
|
25374
|
-
stringValue(metadata['mask']) ||
|
|
25375
|
-
stringValue(metadata['inputMask']) ||
|
|
25376
|
-
maskFromFormat(metadata['format']);
|
|
25377
|
-
if (explicitMask) {
|
|
25378
|
-
if (hasPhoneSignal(metadata, buildComparableSignals(metadata))) {
|
|
25379
|
-
const digits = digitsOnly(currentValue);
|
|
25380
|
-
if (digits.startsWith('55') && digits.length > 11 && explicitMask.includes('+55')) {
|
|
25381
|
-
return {
|
|
25382
|
-
mask: resolveBrazilianPhoneMask(currentValue),
|
|
25383
|
-
rawMode: 'digits',
|
|
25384
|
-
};
|
|
25385
|
-
}
|
|
25386
|
-
}
|
|
25387
|
-
return {
|
|
25388
|
-
mask: explicitMask,
|
|
25389
|
-
rawMode: /[Xx]/.test(explicitMask) ? 'alphanumeric' : 'digits',
|
|
25390
|
-
};
|
|
25391
|
-
}
|
|
25392
|
-
const documentType = normalizeComparableToken(metadata['documentType'] ??
|
|
25393
|
-
metadata['documentKind'] ??
|
|
25394
|
-
metadata['identifierType'] ??
|
|
25395
|
-
metadata['identifierKind']);
|
|
25396
|
-
if (documentType === 'cpf') {
|
|
25397
|
-
return { mask: '000.000.000-00', rawMode: 'digits' };
|
|
25398
|
-
}
|
|
25399
|
-
if (documentType === 'cnpj') {
|
|
25400
|
-
return { mask: '00.000.000/0000-00', rawMode: 'digits' };
|
|
25401
|
-
}
|
|
25402
|
-
const signals = buildComparableSignals(metadata);
|
|
25403
|
-
if (hasSignal(signals, 'cpfcnpj')) {
|
|
25404
|
-
const digits = digitsOnly(currentValue);
|
|
25405
|
-
return {
|
|
25406
|
-
mask: digits.length > 11 ? '00.000.000/0000-00' : '000.000.000-00',
|
|
25407
|
-
rawMode: 'digits',
|
|
25408
|
-
};
|
|
25409
|
-
}
|
|
25410
|
-
if (hasSignal(signals, 'cpf')) {
|
|
25411
|
-
return { mask: '000.000.000-00', rawMode: 'digits' };
|
|
25412
|
-
}
|
|
25413
|
-
if (hasSignal(signals, 'cnpj')) {
|
|
25414
|
-
return { mask: '00.000.000/0000-00', rawMode: 'digits' };
|
|
25415
|
-
}
|
|
25416
|
-
if (hasPhoneSignal(metadata, signals)) {
|
|
25417
|
-
return {
|
|
25418
|
-
mask: resolveBrazilianPhoneMask(currentValue),
|
|
25419
|
-
rawMode: 'digits',
|
|
25420
|
-
};
|
|
25421
|
-
}
|
|
25422
|
-
if (hasPostalCodeSignal(signals)) {
|
|
25423
|
-
return { mask: '00000-000', rawMode: 'digits' };
|
|
25424
|
-
}
|
|
25425
|
-
return null;
|
|
25426
|
-
}
|
|
25427
|
-
function applyInlineDisplayMask(value, mask) {
|
|
25428
|
-
if (!value) {
|
|
25429
|
-
return '';
|
|
25430
|
-
}
|
|
25431
|
-
let valueIndex = 0;
|
|
25432
|
-
let output = '';
|
|
25433
|
-
for (const token of mask.mask) {
|
|
25434
|
-
if (valueIndex >= value.length) {
|
|
25435
|
-
break;
|
|
25436
|
-
}
|
|
25437
|
-
if (isMaskPlaceholder(token)) {
|
|
25438
|
-
output += value[valueIndex++] ?? '';
|
|
25439
|
-
}
|
|
25440
|
-
else {
|
|
25441
|
-
output += token;
|
|
25442
|
-
}
|
|
25443
|
-
}
|
|
25444
|
-
return output;
|
|
25445
|
-
}
|
|
25446
|
-
function unmaskInlineDisplayValue(value, mask) {
|
|
25447
|
-
const text = String(value ?? '');
|
|
25448
|
-
return mask.rawMode === 'alphanumeric'
|
|
25449
|
-
? text.replace(/[^a-zA-Z0-9]/g, '')
|
|
25450
|
-
: digitsOnly(text);
|
|
25451
|
-
}
|
|
25452
|
-
function resolveBrazilianPhoneMask(value) {
|
|
25453
|
-
const digits = digitsOnly(value);
|
|
25454
|
-
if (digits.startsWith('55') && digits.length > 11) {
|
|
25455
|
-
return '+00 00 00000-0000';
|
|
25456
|
-
}
|
|
25457
|
-
return digits.length <= 10 ? '(00) 0000-0000' : '(00) 00000-0000';
|
|
25458
|
-
}
|
|
25459
|
-
function hasPhoneSignal(metadata, signals) {
|
|
25460
|
-
const phoneFormat = normalizeComparableToken(metadata['phoneFormat']);
|
|
25461
|
-
const defaultCountry = normalizeComparableToken(metadata['defaultCountry']);
|
|
25462
|
-
return (phoneFormat === 'national' ||
|
|
25463
|
-
phoneFormat === 'international' ||
|
|
25464
|
-
phoneFormat === 'e164' ||
|
|
25465
|
-
defaultCountry === 'br' ||
|
|
25466
|
-
hasSignal(signals, 'phone') ||
|
|
25467
|
-
hasSignal(signals, 'telefone') ||
|
|
25468
|
-
hasSignal(signals, 'celular') ||
|
|
25469
|
-
hasSignal(signals, 'mobile') ||
|
|
25470
|
-
hasSignal(signals, 'whatsapp') ||
|
|
25471
|
-
hasSignal(signals, 'tel'));
|
|
25472
|
-
}
|
|
25473
|
-
function hasPostalCodeSignal(signals) {
|
|
25474
|
-
return (hasSignal(signals, 'cep') ||
|
|
25475
|
-
hasSignal(signals, 'postalcode') ||
|
|
25476
|
-
hasSignal(signals, 'zipcode') ||
|
|
25477
|
-
hasSignal(signals, 'zip'));
|
|
25478
|
-
}
|
|
25479
|
-
function buildComparableSignals(metadata) {
|
|
25480
|
-
return [
|
|
25481
|
-
metadata['name'],
|
|
25482
|
-
metadata['id'],
|
|
25483
|
-
metadata['key'],
|
|
25484
|
-
metadata['label'],
|
|
25485
|
-
metadata['placeholder'],
|
|
25486
|
-
metadata['controlType'],
|
|
25487
|
-
metadata['type'],
|
|
25488
|
-
metadata['dataType'],
|
|
25489
|
-
metadata['format'],
|
|
25490
|
-
metadata['mask'],
|
|
25491
|
-
metadata['inputMask'],
|
|
25492
|
-
]
|
|
25493
|
-
.map(normalizeComparableToken)
|
|
25494
|
-
.filter(Boolean)
|
|
25495
|
-
.join('|');
|
|
25496
|
-
}
|
|
25497
|
-
function hasSignal(signals, token) {
|
|
25498
|
-
return signals.split('|').includes(token) || signals.includes(token);
|
|
25499
|
-
}
|
|
25500
|
-
function maskFromFormat(value) {
|
|
25501
|
-
const format = stringValue(value);
|
|
25502
|
-
if (!format) {
|
|
25503
|
-
return null;
|
|
25504
|
-
}
|
|
25505
|
-
const hasMaskTokens = /[0#9Xx]/.test(format);
|
|
25506
|
-
const hasInvalidLetters = /[A-WYZawyz]/.test(format);
|
|
25507
|
-
return hasMaskTokens && !hasInvalidLetters ? format : null;
|
|
25508
|
-
}
|
|
25509
|
-
function stringValue(value) {
|
|
25510
|
-
return typeof value === 'string' && value.trim() ? value.trim() : null;
|
|
25511
|
-
}
|
|
25512
|
-
function normalizeComparableToken(value) {
|
|
25513
|
-
return String(value ?? '')
|
|
25514
|
-
.trim()
|
|
25515
|
-
.toLowerCase()
|
|
25516
|
-
.normalize('NFD')
|
|
25517
|
-
.replace(/[\u0300-\u036f]/g, '')
|
|
25518
|
-
.replace(/[\s_-]+/g, '');
|
|
25519
|
-
}
|
|
25520
|
-
function digitsOnly(value) {
|
|
25521
|
-
return String(value ?? '').replace(/\D/g, '');
|
|
25522
|
-
}
|
|
25523
|
-
function isMaskPlaceholder(token) {
|
|
25524
|
-
return token === '0' || token === '9' || token === '#' || token === 'X' || token === 'x';
|
|
25525
|
-
}
|
|
25526
|
-
|
|
25527
25608
|
class InlineInputComponent extends SimpleBaseInputComponent {
|
|
25528
25609
|
readonlyMode = false;
|
|
25529
25610
|
disabledMode = false;
|
package/index.d.ts
CHANGED
|
@@ -705,6 +705,9 @@ declare abstract class SimpleBaseInputComponent implements ControlValueAccessor,
|
|
|
705
705
|
protected getSpecificCssClasses(): string[];
|
|
706
706
|
private setupFormControlIntegration;
|
|
707
707
|
private syncComponentStateFromControl;
|
|
708
|
+
private handleMaskedNativeInput;
|
|
709
|
+
protected applyNativeDisplayMask(value?: unknown): void;
|
|
710
|
+
private resolveNativeDisplayMask;
|
|
708
711
|
private refreshErrorTooltip;
|
|
709
712
|
protected ariaInvalidAttribute(): 'true' | null;
|
|
710
713
|
protected tDynamicFields(keySuffix: string, fallback: string, params?: Record<string, string | number | boolean | null | undefined>): string;
|
|
@@ -1108,6 +1111,7 @@ declare abstract class SimpleBaseSelectComponent<T = any> extends SimpleBaseInpu
|
|
|
1108
1111
|
isSelected(value: T): boolean;
|
|
1109
1112
|
isOptionDisabled(option: SelectOption<T>): boolean;
|
|
1110
1113
|
protected areOptionValuesEqual(left: unknown, right: unknown): boolean;
|
|
1114
|
+
protected normalizeComparableOptionValue(value: unknown): unknown;
|
|
1111
1115
|
/**
|
|
1112
1116
|
* Toggles selection of all options when `selectAll` is enabled in multiple
|
|
1113
1117
|
* selection mode.
|
|
@@ -2143,6 +2147,7 @@ declare class MaterialCurrencyComponent extends SimpleBaseInputComponent {
|
|
|
2143
2147
|
/** Formats the input value without touching or changing focus state. */
|
|
2144
2148
|
formatDisplayValue(): void;
|
|
2145
2149
|
writeValue(value: unknown): void;
|
|
2150
|
+
protected applyNativeDisplayMask(): void;
|
|
2146
2151
|
validateField(): Promise<ValidationErrors | null>;
|
|
2147
2152
|
readonlyMode: boolean;
|
|
2148
2153
|
set disabledMode(value: boolean);
|
|
@@ -5651,6 +5656,7 @@ declare class PhoneInputComponent extends SimpleBaseInputComponent {
|
|
|
5651
5656
|
ngOnInit(): void;
|
|
5652
5657
|
writeValue(value: unknown): void;
|
|
5653
5658
|
protected registerInputElement(el: HTMLElement): void;
|
|
5659
|
+
protected applyNativeDisplayMask(): void;
|
|
5654
5660
|
handleInput(event: Event): void;
|
|
5655
5661
|
handleBlur(): void;
|
|
5656
5662
|
validateField(): Promise<ValidationErrors | null>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@praxisui/dynamic-fields",
|
|
3
|
-
"version": "8.0.0-beta.
|
|
3
|
+
"version": "8.0.0-beta.25",
|
|
4
4
|
"description": "Angular Material-based dynamic form fields for Praxis UI with lazy loading and metadata-driven rendering.",
|
|
5
5
|
"peerDependencies": {
|
|
6
6
|
"@angular/common": "^20.1.0",
|
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
"@angular/platform-browser": "^20.1.0",
|
|
12
12
|
"@angular/router": "^20.1.0",
|
|
13
13
|
"rxjs": "^7.8.0",
|
|
14
|
-
"@praxisui/core": "^8.0.0-beta.
|
|
15
|
-
"@praxisui/cron-builder": "^8.0.0-beta.
|
|
14
|
+
"@praxisui/core": "^8.0.0-beta.25",
|
|
15
|
+
"@praxisui/cron-builder": "^8.0.0-beta.25"
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
18
|
"libphonenumber-js": "^1.12.41",
|