valtech-components 2.0.393 → 2.0.395
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/esm2022/lib/components/molecules/multi-select-simple/multi-select-simple.component.mjs +88 -131
- package/esm2022/lib/components/molecules/select-search/select-search.component.mjs +83 -116
- package/esm2022/lib/components/organisms/form/form.component.mjs +117 -78
- package/fesm2022/valtech-components.mjs +284 -321
- package/fesm2022/valtech-components.mjs.map +1 -1
- package/lib/components/molecules/multi-select-simple/multi-select-simple.component.d.ts +9 -7
- package/lib/components/molecules/select-search/select-search.component.d.ts +9 -6
- package/lib/components/organisms/article/article.component.d.ts +1 -1
- package/lib/components/organisms/form/form.component.d.ts +14 -1
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { EventEmitter, Component, Input, Output, Injectable, inject, InjectionToken, Inject, ChangeDetectorRef, HostListener, Pipe, ChangeDetectionStrategy, ViewChild,
|
|
2
|
+
import { EventEmitter, Component, Input, Output, Injectable, inject, InjectionToken, Inject, ChangeDetectorRef, HostListener, Pipe, ChangeDetectionStrategy, ViewChild, forwardRef } from '@angular/core';
|
|
3
3
|
import { IonAvatar, IonCard, IonIcon, IonButton, IonSpinner, IonText, IonModal, IonHeader, IonToolbar, IonContent, IonButtons, IonTitle, IonProgressBar, IonCardContent, IonCardHeader, IonCardTitle, IonCardSubtitle, IonCheckbox, IonTextarea, IonDatetime, IonDatetimeButton, IonInput, IonSelect, IonSelectOption, IonLabel, IonSearchbar, IonRadioGroup, IonRadio, IonMenuButton, IonFooter, IonList, IonListHeader, IonNote, IonItem } from '@ionic/angular/standalone';
|
|
4
4
|
import * as i1 from '@angular/common';
|
|
5
5
|
import { CommonModule, NgStyle, Location, AsyncPipe, NgFor, NgClass } from '@angular/common';
|
|
@@ -5670,48 +5670,13 @@ class MultiSelectSimpleComponent {
|
|
|
5670
5670
|
this.valueProperty = 'id';
|
|
5671
5671
|
this.placeholder = '';
|
|
5672
5672
|
this.langService = inject(LangService);
|
|
5673
|
-
|
|
5674
|
-
|
|
5675
|
-
this.
|
|
5676
|
-
this.
|
|
5677
|
-
|
|
5678
|
-
this.
|
|
5679
|
-
|
|
5680
|
-
this.displayValue = computed(() => {
|
|
5681
|
-
const selected = this.selectedValues();
|
|
5682
|
-
if (selected.length === 0) {
|
|
5683
|
-
return '';
|
|
5684
|
-
}
|
|
5685
|
-
if (selected.length === 1) {
|
|
5686
|
-
const option = this.getOptionByValue(selected[0]);
|
|
5687
|
-
return option ? option[this.labelProperty] : '';
|
|
5688
|
-
}
|
|
5689
|
-
return `${selected.length} elementos seleccionados`;
|
|
5690
|
-
});
|
|
5691
|
-
this.filteredOptions = computed(() => {
|
|
5692
|
-
// Use signal instead of props directly
|
|
5693
|
-
const options = this.optionsSignal();
|
|
5694
|
-
const search = this.searchTerm().toLowerCase();
|
|
5695
|
-
// Debug
|
|
5696
|
-
console.log('[MultiSelectSimple] filteredOptions computed:', {
|
|
5697
|
-
optionsCount: options.length,
|
|
5698
|
-
searchTerm: search,
|
|
5699
|
-
options: options.slice(0, 3) // Primeras 3 opciones
|
|
5700
|
-
});
|
|
5701
|
-
if (!search) {
|
|
5702
|
-
return options;
|
|
5703
|
-
}
|
|
5704
|
-
return options.filter(option => {
|
|
5705
|
-
const label = option[this.labelProperty]
|
|
5706
|
-
? replaceSpecialChars(String(option[this.labelProperty]).toLowerCase())
|
|
5707
|
-
: '';
|
|
5708
|
-
const value = option[this.valueProperty]
|
|
5709
|
-
? replaceSpecialChars(String(option[this.valueProperty]).toLowerCase())
|
|
5710
|
-
: '';
|
|
5711
|
-
const searchTerm = replaceSpecialChars(search);
|
|
5712
|
-
return label.includes(searchTerm) || value.includes(searchTerm);
|
|
5713
|
-
});
|
|
5714
|
-
});
|
|
5673
|
+
this.changeDetector = inject(ChangeDetectorRef);
|
|
5674
|
+
// Classic Angular properties
|
|
5675
|
+
this.isOpen = false;
|
|
5676
|
+
this.searchTerm = '';
|
|
5677
|
+
this.selectedValues = [];
|
|
5678
|
+
this.displayValue = '';
|
|
5679
|
+
this.filteredOptions = [];
|
|
5715
5680
|
this.searchPlaceholder = '';
|
|
5716
5681
|
// ControlValueAccessor
|
|
5717
5682
|
this.onChange = (_value) => { };
|
|
@@ -5720,52 +5685,29 @@ class MultiSelectSimpleComponent {
|
|
|
5720
5685
|
this.searchPlaceholder = this.langService.getText('_global', 'search', 'Buscar');
|
|
5721
5686
|
// Close dropdown when clicking outside
|
|
5722
5687
|
document.addEventListener('click', this.handleClickOutside.bind(this));
|
|
5723
|
-
// Debug effect to track when filteredOptions changes
|
|
5724
|
-
effect(() => {
|
|
5725
|
-
const options = this.filteredOptions();
|
|
5726
|
-
console.log('[MultiSelectSimple] EFFECT - filteredOptions changed:', {
|
|
5727
|
-
optionsCount: options.length,
|
|
5728
|
-
options: options,
|
|
5729
|
-
timestamp: new Date().toLocaleTimeString()
|
|
5730
|
-
});
|
|
5731
|
-
});
|
|
5732
5688
|
}
|
|
5733
5689
|
ngOnInit() {
|
|
5734
|
-
console.log('[MultiSelectSimple] ngOnInit:', {
|
|
5735
|
-
props: !!this.props,
|
|
5736
|
-
options: this.props?.options?.length || 0,
|
|
5737
|
-
control: !!this.props?.control
|
|
5738
|
-
});
|
|
5739
|
-
// Update options signal
|
|
5740
|
-
if (this.props?.options) {
|
|
5741
|
-
this.optionsSignal.set(this.props.options);
|
|
5742
|
-
}
|
|
5743
5690
|
this.applyDefaultValue();
|
|
5691
|
+
this.initializeOptions();
|
|
5744
5692
|
this.syncSelectedValues();
|
|
5693
|
+
this.updateDisplayValue();
|
|
5745
5694
|
}
|
|
5746
5695
|
ngOnDestroy() {
|
|
5747
5696
|
document.removeEventListener('click', this.handleClickOutside.bind(this));
|
|
5748
5697
|
}
|
|
5749
5698
|
ngOnChanges(changes) {
|
|
5750
|
-
console.log('[MultiSelectSimple] ngOnChanges:', {
|
|
5751
|
-
hasPropsChange: !!changes['props'],
|
|
5752
|
-
props: !!this.props,
|
|
5753
|
-
options: this.props?.options?.length || 0,
|
|
5754
|
-
propsOptions: this.props?.options
|
|
5755
|
-
});
|
|
5756
5699
|
if (changes['props'] && this.props) {
|
|
5757
|
-
|
|
5758
|
-
if (this.props.options) {
|
|
5759
|
-
console.log('[MultiSelectSimple] Updating optionsSignal with:', this.props.options);
|
|
5760
|
-
this.optionsSignal.set(this.props.options);
|
|
5761
|
-
}
|
|
5700
|
+
this.initializeOptions();
|
|
5762
5701
|
this.syncSelectedValues();
|
|
5702
|
+
this.updateDisplayValue();
|
|
5703
|
+
this.changeDetector.detectChanges();
|
|
5763
5704
|
}
|
|
5764
5705
|
}
|
|
5765
5706
|
// ControlValueAccessor implementation
|
|
5766
5707
|
writeValue(value) {
|
|
5767
5708
|
const valueArray = this.parseValue(value);
|
|
5768
|
-
this.selectedValues
|
|
5709
|
+
this.selectedValues = valueArray;
|
|
5710
|
+
this.updateDisplayValue();
|
|
5769
5711
|
}
|
|
5770
5712
|
registerOnChange(fn) {
|
|
5771
5713
|
this.onChange = fn;
|
|
@@ -5779,8 +5721,8 @@ class MultiSelectSimpleComponent {
|
|
|
5779
5721
|
// Component methods
|
|
5780
5722
|
toggleDropdown(event) {
|
|
5781
5723
|
event.stopPropagation();
|
|
5782
|
-
this.isOpen
|
|
5783
|
-
if (this.isOpen
|
|
5724
|
+
this.isOpen = !this.isOpen;
|
|
5725
|
+
if (this.isOpen) {
|
|
5784
5726
|
this.onTouched();
|
|
5785
5727
|
// Focus search bar when opening
|
|
5786
5728
|
setTimeout(() => {
|
|
@@ -5792,45 +5734,48 @@ class MultiSelectSimpleComponent {
|
|
|
5792
5734
|
}
|
|
5793
5735
|
}
|
|
5794
5736
|
onSearch(event) {
|
|
5795
|
-
this.searchTerm
|
|
5737
|
+
this.searchTerm = event.detail.value || '';
|
|
5738
|
+
this.filterOptions();
|
|
5796
5739
|
}
|
|
5797
5740
|
toggleOption(option) {
|
|
5798
|
-
const value = String(option[this.valueProperty]);
|
|
5799
|
-
const currentSelected = this.selectedValues();
|
|
5741
|
+
const value = String(option[this.valueProperty]);
|
|
5800
5742
|
if (this.isSelected(option)) {
|
|
5801
5743
|
// Remove from selection
|
|
5802
|
-
this.selectedValues.
|
|
5744
|
+
this.selectedValues = this.selectedValues.filter(v => v !== value);
|
|
5803
5745
|
}
|
|
5804
5746
|
else {
|
|
5805
5747
|
// Add to selection
|
|
5806
|
-
this.selectedValues
|
|
5748
|
+
this.selectedValues = [...this.selectedValues, value];
|
|
5807
5749
|
}
|
|
5750
|
+
this.updateDisplayValue();
|
|
5808
5751
|
this.emitValue();
|
|
5809
5752
|
}
|
|
5810
5753
|
selectAll() {
|
|
5811
|
-
const allValues = this.filteredOptions
|
|
5812
|
-
const currentSelected = this.selectedValues();
|
|
5754
|
+
const allValues = this.filteredOptions.map(option => String(option[this.valueProperty]));
|
|
5813
5755
|
// Add only new values to avoid duplicates
|
|
5814
|
-
const newValues = allValues.filter(value => !
|
|
5815
|
-
this.selectedValues
|
|
5756
|
+
const newValues = allValues.filter(value => !this.selectedValues.includes(value));
|
|
5757
|
+
this.selectedValues = [...this.selectedValues, ...newValues];
|
|
5758
|
+
this.updateDisplayValue();
|
|
5816
5759
|
this.emitValue();
|
|
5817
5760
|
}
|
|
5818
5761
|
clearAll() {
|
|
5819
|
-
this.selectedValues
|
|
5762
|
+
this.selectedValues = [];
|
|
5763
|
+
this.updateDisplayValue();
|
|
5820
5764
|
this.emitValue();
|
|
5821
5765
|
}
|
|
5822
5766
|
isSelected(option) {
|
|
5823
|
-
return this.selectedValues
|
|
5767
|
+
return this.selectedValues.includes(String(option[this.valueProperty]));
|
|
5824
5768
|
}
|
|
5825
5769
|
trackByFn(_index, option) {
|
|
5826
5770
|
return option[this.valueProperty];
|
|
5827
5771
|
}
|
|
5828
5772
|
handleClickOutside(event) {
|
|
5829
|
-
if (this.isOpen
|
|
5773
|
+
if (this.isOpen &&
|
|
5830
5774
|
!this.mainInputRef?.nativeElement?.contains(event.target) &&
|
|
5831
5775
|
!this.dropdownRef?.nativeElement?.contains(event.target)) {
|
|
5832
|
-
this.isOpen
|
|
5833
|
-
this.searchTerm
|
|
5776
|
+
this.isOpen = false;
|
|
5777
|
+
this.searchTerm = '';
|
|
5778
|
+
this.initializeOptions();
|
|
5834
5779
|
}
|
|
5835
5780
|
}
|
|
5836
5781
|
parseValue(value) {
|
|
@@ -5844,7 +5789,7 @@ class MultiSelectSimpleComponent {
|
|
|
5844
5789
|
return [String(value)];
|
|
5845
5790
|
}
|
|
5846
5791
|
emitValue() {
|
|
5847
|
-
const value = this.selectedValues
|
|
5792
|
+
const value = this.selectedValues.join(',');
|
|
5848
5793
|
this.onChange(value);
|
|
5849
5794
|
if (this.props?.control) {
|
|
5850
5795
|
this.props.control.setValue(value);
|
|
@@ -5853,27 +5798,49 @@ class MultiSelectSimpleComponent {
|
|
|
5853
5798
|
}
|
|
5854
5799
|
}
|
|
5855
5800
|
getOptionByValue(value) {
|
|
5856
|
-
return this.
|
|
5801
|
+
return this.filteredOptions.find(option => String(option[this.valueProperty]) === String(value));
|
|
5857
5802
|
}
|
|
5858
5803
|
applyDefaultValue() {
|
|
5859
5804
|
if (this.props) {
|
|
5860
5805
|
applyDefaultValueToControl(this.props);
|
|
5861
5806
|
}
|
|
5862
5807
|
}
|
|
5863
|
-
|
|
5864
|
-
|
|
5865
|
-
|
|
5866
|
-
|
|
5867
|
-
|
|
5868
|
-
|
|
5869
|
-
|
|
5870
|
-
|
|
5808
|
+
initializeOptions() {
|
|
5809
|
+
this.filteredOptions = this.props?.options || [];
|
|
5810
|
+
}
|
|
5811
|
+
filterOptions() {
|
|
5812
|
+
if (!this.searchTerm) {
|
|
5813
|
+
this.initializeOptions();
|
|
5814
|
+
return;
|
|
5815
|
+
}
|
|
5816
|
+
const search = replaceSpecialChars(this.searchTerm.toLowerCase());
|
|
5817
|
+
this.filteredOptions = (this.props?.options || []).filter(option => {
|
|
5818
|
+
const label = option[this.labelProperty]
|
|
5819
|
+
? replaceSpecialChars(String(option[this.labelProperty]).toLowerCase())
|
|
5820
|
+
: '';
|
|
5821
|
+
const value = option[this.valueProperty]
|
|
5822
|
+
? replaceSpecialChars(String(option[this.valueProperty]).toLowerCase())
|
|
5823
|
+
: '';
|
|
5824
|
+
return label.includes(search) || value.includes(search);
|
|
5871
5825
|
});
|
|
5872
5826
|
}
|
|
5827
|
+
updateDisplayValue() {
|
|
5828
|
+
if (this.selectedValues.length === 0) {
|
|
5829
|
+
this.displayValue = '';
|
|
5830
|
+
return;
|
|
5831
|
+
}
|
|
5832
|
+
if (this.selectedValues.length === 1) {
|
|
5833
|
+
const option = this.getOptionByValue(this.selectedValues[0]);
|
|
5834
|
+
this.displayValue = option ? option[this.labelProperty] : '';
|
|
5835
|
+
}
|
|
5836
|
+
else {
|
|
5837
|
+
this.displayValue = `${this.selectedValues.length} elementos seleccionados`;
|
|
5838
|
+
}
|
|
5839
|
+
}
|
|
5873
5840
|
syncSelectedValues() {
|
|
5874
5841
|
if (this.props?.control?.value) {
|
|
5875
5842
|
const valueArray = this.parseValue(this.props.control.value);
|
|
5876
|
-
this.selectedValues
|
|
5843
|
+
this.selectedValues = valueArray;
|
|
5877
5844
|
}
|
|
5878
5845
|
}
|
|
5879
5846
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MultiSelectSimpleComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
@@ -5889,18 +5856,18 @@ class MultiSelectSimpleComponent {
|
|
|
5889
5856
|
<ion-input
|
|
5890
5857
|
#mainInput
|
|
5891
5858
|
type="text"
|
|
5892
|
-
[value]="displayValue
|
|
5859
|
+
[value]="displayValue"
|
|
5893
5860
|
[placeholder]="props?.placeholder || placeholder"
|
|
5894
5861
|
readonly
|
|
5895
5862
|
class="main-input"
|
|
5896
|
-
[class.is-open]="isOpen
|
|
5863
|
+
[class.is-open]="isOpen"
|
|
5897
5864
|
/>
|
|
5898
5865
|
|
|
5899
5866
|
<!-- Dropdown icon -->
|
|
5900
5867
|
<ion-icon
|
|
5901
5868
|
name="chevron-down-outline"
|
|
5902
5869
|
class="dropdown-icon"
|
|
5903
|
-
[class.rotated]="isOpen
|
|
5870
|
+
[class.rotated]="isOpen"
|
|
5904
5871
|
></ion-icon>
|
|
5905
5872
|
|
|
5906
5873
|
<!-- Hidden input for form control -->
|
|
@@ -5914,7 +5881,7 @@ class MultiSelectSimpleComponent {
|
|
|
5914
5881
|
<!-- Dropdown overlay -->
|
|
5915
5882
|
<div
|
|
5916
5883
|
class="dropdown-overlay"
|
|
5917
|
-
[class.visible]="isOpen
|
|
5884
|
+
[class.visible]="isOpen"
|
|
5918
5885
|
#dropdown
|
|
5919
5886
|
>
|
|
5920
5887
|
<!-- Search bar -->
|
|
@@ -5923,7 +5890,7 @@ class MultiSelectSimpleComponent {
|
|
|
5923
5890
|
#searchbar
|
|
5924
5891
|
[placeholder]="searchPlaceholder"
|
|
5925
5892
|
(ionInput)="onSearch($event)"
|
|
5926
|
-
[value]="searchTerm
|
|
5893
|
+
[value]="searchTerm"
|
|
5927
5894
|
show-clear-button="focus"
|
|
5928
5895
|
></ion-searchbar>
|
|
5929
5896
|
</div>
|
|
@@ -5934,7 +5901,7 @@ class MultiSelectSimpleComponent {
|
|
|
5934
5901
|
fill="clear"
|
|
5935
5902
|
size="small"
|
|
5936
5903
|
(click)="selectAll()"
|
|
5937
|
-
[disabled]="filteredOptions
|
|
5904
|
+
[disabled]="filteredOptions.length === 0"
|
|
5938
5905
|
>
|
|
5939
5906
|
Seleccionar todos
|
|
5940
5907
|
</ion-button>
|
|
@@ -5943,22 +5910,17 @@ class MultiSelectSimpleComponent {
|
|
|
5943
5910
|
size="small"
|
|
5944
5911
|
color="medium"
|
|
5945
5912
|
(click)="clearAll()"
|
|
5946
|
-
[disabled]="selectedValues
|
|
5913
|
+
[disabled]="selectedValues.length === 0"
|
|
5947
5914
|
>
|
|
5948
5915
|
Limpiar
|
|
5949
5916
|
</ion-button>
|
|
5950
5917
|
</div>
|
|
5951
5918
|
|
|
5952
|
-
<!-- Debug button -->
|
|
5953
|
-
<div class="search-container" style="background: red; color: white; padding: 4px;">
|
|
5954
|
-
<button (click)="debugOptions()">DEBUG: {{ filteredOptions().length }} opciones</button>
|
|
5955
|
-
</div>
|
|
5956
|
-
|
|
5957
5919
|
<!-- Options list -->
|
|
5958
5920
|
<div class="options-container">
|
|
5959
5921
|
<ion-list class="options-list">
|
|
5960
5922
|
<ion-item
|
|
5961
|
-
*ngFor="let option of filteredOptions
|
|
5923
|
+
*ngFor="let option of filteredOptions; trackBy: trackByFn"
|
|
5962
5924
|
button
|
|
5963
5925
|
(click)="toggleOption(option)"
|
|
5964
5926
|
class="option-item"
|
|
@@ -5971,9 +5933,9 @@ class MultiSelectSimpleComponent {
|
|
|
5971
5933
|
</ion-item>
|
|
5972
5934
|
|
|
5973
5935
|
<!-- No results message -->
|
|
5974
|
-
<ion-item *ngIf="filteredOptions
|
|
5936
|
+
<ion-item *ngIf="filteredOptions.length === 0" class="no-results">
|
|
5975
5937
|
<ion-label color="medium">
|
|
5976
|
-
{{ searchTerm
|
|
5938
|
+
{{ searchTerm ? 'No se encontraron resultados' : 'No hay opciones disponibles' }}
|
|
5977
5939
|
</ion-label>
|
|
5978
5940
|
</ion-item>
|
|
5979
5941
|
</ion-list>
|
|
@@ -5995,18 +5957,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
|
|
|
5995
5957
|
<ion-input
|
|
5996
5958
|
#mainInput
|
|
5997
5959
|
type="text"
|
|
5998
|
-
[value]="displayValue
|
|
5960
|
+
[value]="displayValue"
|
|
5999
5961
|
[placeholder]="props?.placeholder || placeholder"
|
|
6000
5962
|
readonly
|
|
6001
5963
|
class="main-input"
|
|
6002
|
-
[class.is-open]="isOpen
|
|
5964
|
+
[class.is-open]="isOpen"
|
|
6003
5965
|
/>
|
|
6004
5966
|
|
|
6005
5967
|
<!-- Dropdown icon -->
|
|
6006
5968
|
<ion-icon
|
|
6007
5969
|
name="chevron-down-outline"
|
|
6008
5970
|
class="dropdown-icon"
|
|
6009
|
-
[class.rotated]="isOpen
|
|
5971
|
+
[class.rotated]="isOpen"
|
|
6010
5972
|
></ion-icon>
|
|
6011
5973
|
|
|
6012
5974
|
<!-- Hidden input for form control -->
|
|
@@ -6020,7 +5982,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
|
|
|
6020
5982
|
<!-- Dropdown overlay -->
|
|
6021
5983
|
<div
|
|
6022
5984
|
class="dropdown-overlay"
|
|
6023
|
-
[class.visible]="isOpen
|
|
5985
|
+
[class.visible]="isOpen"
|
|
6024
5986
|
#dropdown
|
|
6025
5987
|
>
|
|
6026
5988
|
<!-- Search bar -->
|
|
@@ -6029,7 +5991,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
|
|
|
6029
5991
|
#searchbar
|
|
6030
5992
|
[placeholder]="searchPlaceholder"
|
|
6031
5993
|
(ionInput)="onSearch($event)"
|
|
6032
|
-
[value]="searchTerm
|
|
5994
|
+
[value]="searchTerm"
|
|
6033
5995
|
show-clear-button="focus"
|
|
6034
5996
|
></ion-searchbar>
|
|
6035
5997
|
</div>
|
|
@@ -6040,7 +6002,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
|
|
|
6040
6002
|
fill="clear"
|
|
6041
6003
|
size="small"
|
|
6042
6004
|
(click)="selectAll()"
|
|
6043
|
-
[disabled]="filteredOptions
|
|
6005
|
+
[disabled]="filteredOptions.length === 0"
|
|
6044
6006
|
>
|
|
6045
6007
|
Seleccionar todos
|
|
6046
6008
|
</ion-button>
|
|
@@ -6049,22 +6011,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
|
|
|
6049
6011
|
size="small"
|
|
6050
6012
|
color="medium"
|
|
6051
6013
|
(click)="clearAll()"
|
|
6052
|
-
[disabled]="selectedValues
|
|
6014
|
+
[disabled]="selectedValues.length === 0"
|
|
6053
6015
|
>
|
|
6054
6016
|
Limpiar
|
|
6055
6017
|
</ion-button>
|
|
6056
6018
|
</div>
|
|
6057
6019
|
|
|
6058
|
-
<!-- Debug button -->
|
|
6059
|
-
<div class="search-container" style="background: red; color: white; padding: 4px;">
|
|
6060
|
-
<button (click)="debugOptions()">DEBUG: {{ filteredOptions().length }} opciones</button>
|
|
6061
|
-
</div>
|
|
6062
|
-
|
|
6063
6020
|
<!-- Options list -->
|
|
6064
6021
|
<div class="options-container">
|
|
6065
6022
|
<ion-list class="options-list">
|
|
6066
6023
|
<ion-item
|
|
6067
|
-
*ngFor="let option of filteredOptions
|
|
6024
|
+
*ngFor="let option of filteredOptions; trackBy: trackByFn"
|
|
6068
6025
|
button
|
|
6069
6026
|
(click)="toggleOption(option)"
|
|
6070
6027
|
class="option-item"
|
|
@@ -6077,9 +6034,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
|
|
|
6077
6034
|
</ion-item>
|
|
6078
6035
|
|
|
6079
6036
|
<!-- No results message -->
|
|
6080
|
-
<ion-item *ngIf="filteredOptions
|
|
6037
|
+
<ion-item *ngIf="filteredOptions.length === 0" class="no-results">
|
|
6081
6038
|
<ion-label color="medium">
|
|
6082
|
-
{{ searchTerm
|
|
6039
|
+
{{ searchTerm ? 'No se encontraron resultados' : 'No hay opciones disponibles' }}
|
|
6083
6040
|
</ion-label>
|
|
6084
6041
|
</ion-item>
|
|
6085
6042
|
</ion-list>
|
|
@@ -6817,105 +6774,39 @@ class SelectSearchComponent {
|
|
|
6817
6774
|
this.valueProperty = 'id';
|
|
6818
6775
|
this.placeholder = '';
|
|
6819
6776
|
this.langService = inject(LangService);
|
|
6820
|
-
|
|
6821
|
-
|
|
6822
|
-
this.
|
|
6823
|
-
this.
|
|
6824
|
-
|
|
6825
|
-
this.
|
|
6826
|
-
|
|
6827
|
-
this.displayValue = computed(() => {
|
|
6828
|
-
const value = this.selectedValue();
|
|
6829
|
-
if (!value)
|
|
6830
|
-
return '';
|
|
6831
|
-
const option = this.getOptionByValue(value);
|
|
6832
|
-
return option ? option[this.labelProperty] : '';
|
|
6833
|
-
});
|
|
6834
|
-
this.filteredOptions = computed(() => {
|
|
6835
|
-
// Use signal instead of props directly
|
|
6836
|
-
const options = this.optionsSignal();
|
|
6837
|
-
const search = this.searchTerm().toLowerCase();
|
|
6838
|
-
// Debug mejorado
|
|
6839
|
-
console.log('[SelectSearch] filteredOptions computed:', {
|
|
6840
|
-
optionsCount: options.length,
|
|
6841
|
-
searchTerm: search,
|
|
6842
|
-
options: options,
|
|
6843
|
-
labelProperty: this.labelProperty,
|
|
6844
|
-
valueProperty: this.valueProperty,
|
|
6845
|
-
propsExists: !!this.props,
|
|
6846
|
-
propsOptionsExists: !!this.props?.options,
|
|
6847
|
-
propsOptionsLength: this.props?.options?.length || 0,
|
|
6848
|
-
firstOptionStructure: options[0] ? {
|
|
6849
|
-
[this.labelProperty]: options[0][this.labelProperty],
|
|
6850
|
-
[this.valueProperty]: options[0][this.valueProperty],
|
|
6851
|
-
typeOfId: typeof options[0][this.valueProperty],
|
|
6852
|
-
fullOption: options[0]
|
|
6853
|
-
} : 'NO_OPTIONS'
|
|
6854
|
-
});
|
|
6855
|
-
if (!search) {
|
|
6856
|
-
return options;
|
|
6857
|
-
}
|
|
6858
|
-
return options.filter(option => {
|
|
6859
|
-
const label = option[this.labelProperty]
|
|
6860
|
-
? replaceSpecialChars(String(option[this.labelProperty]).toLowerCase())
|
|
6861
|
-
: '';
|
|
6862
|
-
const value = option[this.valueProperty]
|
|
6863
|
-
? replaceSpecialChars(String(option[this.valueProperty]).toLowerCase())
|
|
6864
|
-
: '';
|
|
6865
|
-
const searchTerm = replaceSpecialChars(search);
|
|
6866
|
-
return label.includes(searchTerm) || value.includes(searchTerm);
|
|
6867
|
-
});
|
|
6868
|
-
});
|
|
6777
|
+
this.changeDetector = inject(ChangeDetectorRef);
|
|
6778
|
+
// Classic Angular properties
|
|
6779
|
+
this.isOpen = false;
|
|
6780
|
+
this.searchTerm = '';
|
|
6781
|
+
this.selectedValue = null;
|
|
6782
|
+
this.displayValue = '';
|
|
6783
|
+
this.filteredOptions = [];
|
|
6869
6784
|
this.placeholder = this.langService.getText('_global', 'selectOption', 'Seleccione una opción');
|
|
6870
6785
|
// Close dropdown when clicking outside
|
|
6871
6786
|
document.addEventListener('click', this.handleClickOutside.bind(this));
|
|
6872
|
-
// Debug effect to track when filteredOptions changes
|
|
6873
|
-
effect(() => {
|
|
6874
|
-
const options = this.filteredOptions();
|
|
6875
|
-
console.log('[SelectSearch] EFFECT - filteredOptions changed:', {
|
|
6876
|
-
optionsCount: options.length,
|
|
6877
|
-
options: options,
|
|
6878
|
-
timestamp: new Date().toLocaleTimeString()
|
|
6879
|
-
});
|
|
6880
|
-
});
|
|
6881
6787
|
}
|
|
6882
6788
|
ngOnInit() {
|
|
6883
|
-
console.log('[SelectSearch] ngOnInit:', {
|
|
6884
|
-
props: !!this.props,
|
|
6885
|
-
options: this.props?.options?.length || 0,
|
|
6886
|
-
control: !!this.props?.control
|
|
6887
|
-
});
|
|
6888
|
-
// Update options signal
|
|
6889
|
-
if (this.props?.options) {
|
|
6890
|
-
this.optionsSignal.set(this.props.options);
|
|
6891
|
-
}
|
|
6892
6789
|
this.applyDefaultValue();
|
|
6790
|
+
this.initializeOptions();
|
|
6893
6791
|
this.syncSelectedValue();
|
|
6792
|
+
this.updateDisplayValue();
|
|
6894
6793
|
}
|
|
6895
6794
|
ngOnDestroy() {
|
|
6896
6795
|
document.removeEventListener('click', this.handleClickOutside.bind(this));
|
|
6897
6796
|
}
|
|
6898
6797
|
ngOnChanges(changes) {
|
|
6899
|
-
console.log('[SelectSearch] ngOnChanges:', {
|
|
6900
|
-
hasPropsChange: !!changes['props'],
|
|
6901
|
-
props: !!this.props,
|
|
6902
|
-
options: this.props?.options?.length || 0,
|
|
6903
|
-
propsOptions: this.props?.options
|
|
6904
|
-
});
|
|
6905
6798
|
if (changes['props'] && this.props) {
|
|
6906
|
-
|
|
6907
|
-
if (this.props.options) {
|
|
6908
|
-
console.log('[SelectSearch] Updating optionsSignal with:', this.props.options);
|
|
6909
|
-
this.optionsSignal.set(this.props.options);
|
|
6910
|
-
}
|
|
6799
|
+
this.initializeOptions();
|
|
6911
6800
|
this.syncSelectedValue();
|
|
6801
|
+
this.updateDisplayValue();
|
|
6802
|
+
this.changeDetector.detectChanges();
|
|
6912
6803
|
}
|
|
6913
6804
|
}
|
|
6914
6805
|
// Component methods
|
|
6915
6806
|
toggleDropdown(event) {
|
|
6916
6807
|
event.stopPropagation();
|
|
6917
|
-
this.isOpen
|
|
6918
|
-
if (this.isOpen
|
|
6808
|
+
this.isOpen = !this.isOpen;
|
|
6809
|
+
if (this.isOpen) {
|
|
6919
6810
|
// Focus search bar when opening if available
|
|
6920
6811
|
setTimeout(() => {
|
|
6921
6812
|
const searchbar = this.dropdownRef?.nativeElement?.querySelector('ion-searchbar');
|
|
@@ -6926,13 +6817,16 @@ class SelectSearchComponent {
|
|
|
6926
6817
|
}
|
|
6927
6818
|
}
|
|
6928
6819
|
onSearch(event) {
|
|
6929
|
-
this.searchTerm
|
|
6820
|
+
this.searchTerm = event.detail.value || '';
|
|
6821
|
+
this.filterOptions();
|
|
6930
6822
|
}
|
|
6931
6823
|
selectOption(option) {
|
|
6932
|
-
const value = String(option[this.valueProperty]);
|
|
6933
|
-
this.selectedValue
|
|
6934
|
-
this.isOpen
|
|
6935
|
-
this.searchTerm
|
|
6824
|
+
const value = String(option[this.valueProperty]);
|
|
6825
|
+
this.selectedValue = value;
|
|
6826
|
+
this.isOpen = false;
|
|
6827
|
+
this.searchTerm = '';
|
|
6828
|
+
this.updateDisplayValue();
|
|
6829
|
+
this.initializeOptions(); // Reset filter
|
|
6936
6830
|
// Update form control
|
|
6937
6831
|
if (this.props?.control) {
|
|
6938
6832
|
this.props.control.setValue(value);
|
|
@@ -6941,21 +6835,49 @@ class SelectSearchComponent {
|
|
|
6941
6835
|
}
|
|
6942
6836
|
}
|
|
6943
6837
|
isSelected(option) {
|
|
6944
|
-
return String(this.selectedValue
|
|
6838
|
+
return String(this.selectedValue) === String(option[this.valueProperty]);
|
|
6945
6839
|
}
|
|
6946
6840
|
trackByFn(_index, option) {
|
|
6947
6841
|
return option[this.valueProperty];
|
|
6948
6842
|
}
|
|
6949
6843
|
handleClickOutside(event) {
|
|
6950
|
-
if (this.isOpen
|
|
6844
|
+
if (this.isOpen &&
|
|
6951
6845
|
!this.mainInputRef?.nativeElement?.contains(event.target) &&
|
|
6952
6846
|
!this.dropdownRef?.nativeElement?.contains(event.target)) {
|
|
6953
|
-
this.isOpen
|
|
6954
|
-
this.searchTerm
|
|
6847
|
+
this.isOpen = false;
|
|
6848
|
+
this.searchTerm = '';
|
|
6849
|
+
this.initializeOptions();
|
|
6955
6850
|
}
|
|
6956
6851
|
}
|
|
6957
6852
|
getOptionByValue(value) {
|
|
6958
|
-
return this.
|
|
6853
|
+
return this.filteredOptions.find(option => String(option[this.valueProperty]) === String(value));
|
|
6854
|
+
}
|
|
6855
|
+
initializeOptions() {
|
|
6856
|
+
this.filteredOptions = this.props?.options || [];
|
|
6857
|
+
}
|
|
6858
|
+
filterOptions() {
|
|
6859
|
+
if (!this.searchTerm) {
|
|
6860
|
+
this.initializeOptions();
|
|
6861
|
+
return;
|
|
6862
|
+
}
|
|
6863
|
+
const search = replaceSpecialChars(this.searchTerm.toLowerCase());
|
|
6864
|
+
this.filteredOptions = (this.props?.options || []).filter(option => {
|
|
6865
|
+
const label = option[this.labelProperty]
|
|
6866
|
+
? replaceSpecialChars(String(option[this.labelProperty]).toLowerCase())
|
|
6867
|
+
: '';
|
|
6868
|
+
const value = option[this.valueProperty]
|
|
6869
|
+
? replaceSpecialChars(String(option[this.valueProperty]).toLowerCase())
|
|
6870
|
+
: '';
|
|
6871
|
+
return label.includes(search) || value.includes(search);
|
|
6872
|
+
});
|
|
6873
|
+
}
|
|
6874
|
+
updateDisplayValue() {
|
|
6875
|
+
if (!this.selectedValue) {
|
|
6876
|
+
this.displayValue = '';
|
|
6877
|
+
return;
|
|
6878
|
+
}
|
|
6879
|
+
const option = this.getOptionByValue(this.selectedValue);
|
|
6880
|
+
this.displayValue = option ? option[this.labelProperty] : '';
|
|
6959
6881
|
}
|
|
6960
6882
|
applyDefaultValue() {
|
|
6961
6883
|
if (this.props) {
|
|
@@ -6965,15 +6887,17 @@ class SelectSearchComponent {
|
|
|
6965
6887
|
debugOptions() {
|
|
6966
6888
|
console.log('[SelectSearch] DEBUG CLICK:', {
|
|
6967
6889
|
props: this.props,
|
|
6968
|
-
filteredOptions: this.filteredOptions
|
|
6969
|
-
searchTerm: this.searchTerm
|
|
6890
|
+
filteredOptions: this.filteredOptions,
|
|
6891
|
+
searchTerm: this.searchTerm,
|
|
6970
6892
|
propsOptions: this.props?.options,
|
|
6971
|
-
|
|
6893
|
+
displayValue: this.displayValue,
|
|
6894
|
+
selectedValue: this.selectedValue,
|
|
6895
|
+
isOpen: this.isOpen
|
|
6972
6896
|
});
|
|
6973
6897
|
}
|
|
6974
6898
|
syncSelectedValue() {
|
|
6975
6899
|
if (this.props?.control?.value) {
|
|
6976
|
-
this.selectedValue
|
|
6900
|
+
this.selectedValue = String(this.props.control.value);
|
|
6977
6901
|
}
|
|
6978
6902
|
}
|
|
6979
6903
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SelectSearchComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
@@ -6983,18 +6907,18 @@ class SelectSearchComponent {
|
|
|
6983
6907
|
<ion-input
|
|
6984
6908
|
#mainInput
|
|
6985
6909
|
type="text"
|
|
6986
|
-
[value]="displayValue
|
|
6910
|
+
[value]="displayValue"
|
|
6987
6911
|
[placeholder]="props?.placeholder || placeholder"
|
|
6988
6912
|
readonly
|
|
6989
6913
|
class="main-input"
|
|
6990
|
-
[class.is-open]="isOpen
|
|
6914
|
+
[class.is-open]="isOpen"
|
|
6991
6915
|
/>
|
|
6992
6916
|
|
|
6993
6917
|
<!-- Dropdown icon -->
|
|
6994
6918
|
<ion-icon
|
|
6995
6919
|
name="chevron-down-outline"
|
|
6996
6920
|
class="dropdown-icon"
|
|
6997
|
-
[class.rotated]="isOpen
|
|
6921
|
+
[class.rotated]="isOpen"
|
|
6998
6922
|
></ion-icon>
|
|
6999
6923
|
|
|
7000
6924
|
<!-- Hidden input for form control -->
|
|
@@ -7008,12 +6932,12 @@ class SelectSearchComponent {
|
|
|
7008
6932
|
<!-- Dropdown overlay -->
|
|
7009
6933
|
<div
|
|
7010
6934
|
class="dropdown-overlay"
|
|
7011
|
-
[class.visible]="isOpen
|
|
6935
|
+
[class.visible]="isOpen"
|
|
7012
6936
|
#dropdown
|
|
7013
6937
|
>
|
|
7014
6938
|
<!-- Debug button -->
|
|
7015
6939
|
<div class="search-container" style="background: red; color: white; padding: 4px;">
|
|
7016
|
-
<button (click)="debugOptions()">DEBUG: {{ filteredOptions
|
|
6940
|
+
<button (click)="debugOptions()">DEBUG: {{ filteredOptions.length }} opciones</button>
|
|
7017
6941
|
</div>
|
|
7018
6942
|
|
|
7019
6943
|
<!-- Search bar -->
|
|
@@ -7022,7 +6946,7 @@ class SelectSearchComponent {
|
|
|
7022
6946
|
#searchbar
|
|
7023
6947
|
[placeholder]="'Buscar'"
|
|
7024
6948
|
(ionInput)="onSearch($event)"
|
|
7025
|
-
[value]="searchTerm
|
|
6949
|
+
[value]="searchTerm"
|
|
7026
6950
|
show-clear-button="focus"
|
|
7027
6951
|
[debounce]="200"
|
|
7028
6952
|
></ion-searchbar>
|
|
@@ -7032,7 +6956,7 @@ class SelectSearchComponent {
|
|
|
7032
6956
|
<div class="options-container">
|
|
7033
6957
|
<ion-list class="options-list">
|
|
7034
6958
|
<ion-item
|
|
7035
|
-
*ngFor="let option of filteredOptions
|
|
6959
|
+
*ngFor="let option of filteredOptions; trackBy: trackByFn"
|
|
7036
6960
|
button
|
|
7037
6961
|
(click)="selectOption(option)"
|
|
7038
6962
|
class="option-item"
|
|
@@ -7047,11 +6971,11 @@ class SelectSearchComponent {
|
|
|
7047
6971
|
</ion-item>
|
|
7048
6972
|
|
|
7049
6973
|
<!-- No results message -->
|
|
7050
|
-
<ion-item *ngIf="filteredOptions
|
|
6974
|
+
<ion-item *ngIf="filteredOptions.length === 0" class="no-results">
|
|
7051
6975
|
<ion-label color="medium">
|
|
7052
|
-
{{ searchTerm
|
|
6976
|
+
{{ searchTerm ? 'No se encontraron resultados' : 'No hay opciones disponibles' }}
|
|
7053
6977
|
<!-- Debug info -->
|
|
7054
|
-
<br><small>Debug: {{ filteredOptions
|
|
6978
|
+
<br><small>Debug: {{ filteredOptions.length }} opciones | Props: {{ !!props }} | Search: "{{ searchTerm }}"</small>
|
|
7055
6979
|
</ion-label>
|
|
7056
6980
|
</ion-item>
|
|
7057
6981
|
</ion-list>
|
|
@@ -7067,18 +6991,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
|
|
|
7067
6991
|
<ion-input
|
|
7068
6992
|
#mainInput
|
|
7069
6993
|
type="text"
|
|
7070
|
-
[value]="displayValue
|
|
6994
|
+
[value]="displayValue"
|
|
7071
6995
|
[placeholder]="props?.placeholder || placeholder"
|
|
7072
6996
|
readonly
|
|
7073
6997
|
class="main-input"
|
|
7074
|
-
[class.is-open]="isOpen
|
|
6998
|
+
[class.is-open]="isOpen"
|
|
7075
6999
|
/>
|
|
7076
7000
|
|
|
7077
7001
|
<!-- Dropdown icon -->
|
|
7078
7002
|
<ion-icon
|
|
7079
7003
|
name="chevron-down-outline"
|
|
7080
7004
|
class="dropdown-icon"
|
|
7081
|
-
[class.rotated]="isOpen
|
|
7005
|
+
[class.rotated]="isOpen"
|
|
7082
7006
|
></ion-icon>
|
|
7083
7007
|
|
|
7084
7008
|
<!-- Hidden input for form control -->
|
|
@@ -7092,12 +7016,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
|
|
|
7092
7016
|
<!-- Dropdown overlay -->
|
|
7093
7017
|
<div
|
|
7094
7018
|
class="dropdown-overlay"
|
|
7095
|
-
[class.visible]="isOpen
|
|
7019
|
+
[class.visible]="isOpen"
|
|
7096
7020
|
#dropdown
|
|
7097
7021
|
>
|
|
7098
7022
|
<!-- Debug button -->
|
|
7099
7023
|
<div class="search-container" style="background: red; color: white; padding: 4px;">
|
|
7100
|
-
<button (click)="debugOptions()">DEBUG: {{ filteredOptions
|
|
7024
|
+
<button (click)="debugOptions()">DEBUG: {{ filteredOptions.length }} opciones</button>
|
|
7101
7025
|
</div>
|
|
7102
7026
|
|
|
7103
7027
|
<!-- Search bar -->
|
|
@@ -7106,7 +7030,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
|
|
|
7106
7030
|
#searchbar
|
|
7107
7031
|
[placeholder]="'Buscar'"
|
|
7108
7032
|
(ionInput)="onSearch($event)"
|
|
7109
|
-
[value]="searchTerm
|
|
7033
|
+
[value]="searchTerm"
|
|
7110
7034
|
show-clear-button="focus"
|
|
7111
7035
|
[debounce]="200"
|
|
7112
7036
|
></ion-searchbar>
|
|
@@ -7116,7 +7040,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
|
|
|
7116
7040
|
<div class="options-container">
|
|
7117
7041
|
<ion-list class="options-list">
|
|
7118
7042
|
<ion-item
|
|
7119
|
-
*ngFor="let option of filteredOptions
|
|
7043
|
+
*ngFor="let option of filteredOptions; trackBy: trackByFn"
|
|
7120
7044
|
button
|
|
7121
7045
|
(click)="selectOption(option)"
|
|
7122
7046
|
class="option-item"
|
|
@@ -7131,11 +7055,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
|
|
|
7131
7055
|
</ion-item>
|
|
7132
7056
|
|
|
7133
7057
|
<!-- No results message -->
|
|
7134
|
-
<ion-item *ngIf="filteredOptions
|
|
7058
|
+
<ion-item *ngIf="filteredOptions.length === 0" class="no-results">
|
|
7135
7059
|
<ion-label color="medium">
|
|
7136
|
-
{{ searchTerm
|
|
7060
|
+
{{ searchTerm ? 'No se encontraron resultados' : 'No hay opciones disponibles' }}
|
|
7137
7061
|
<!-- Debug info -->
|
|
7138
|
-
<br><small>Debug: {{ filteredOptions
|
|
7062
|
+
<br><small>Debug: {{ filteredOptions.length }} opciones | Props: {{ !!props }} | Search: "{{ searchTerm }}"</small>
|
|
7139
7063
|
</ion-label>
|
|
7140
7064
|
</ion-item>
|
|
7141
7065
|
</ion-list>
|
|
@@ -8325,9 +8249,16 @@ class FormComponent {
|
|
|
8325
8249
|
this.onSelectChange = new EventEmitter();
|
|
8326
8250
|
this.types = InputType;
|
|
8327
8251
|
this.subscriptions = [];
|
|
8252
|
+
// Cache processed field properties
|
|
8253
|
+
this.processedSections = [];
|
|
8328
8254
|
this.previousValues = new Map();
|
|
8329
8255
|
}
|
|
8330
8256
|
ngOnInit() {
|
|
8257
|
+
this.createForm();
|
|
8258
|
+
this.processAllSections();
|
|
8259
|
+
this.setupChangeTracking();
|
|
8260
|
+
}
|
|
8261
|
+
createForm() {
|
|
8331
8262
|
const formControls = {};
|
|
8332
8263
|
this.props.sections.forEach(section => {
|
|
8333
8264
|
section.fields.forEach(field => {
|
|
@@ -8342,6 +8273,60 @@ class FormComponent {
|
|
|
8342
8273
|
});
|
|
8343
8274
|
});
|
|
8344
8275
|
this.form = this.fb.group(formControls);
|
|
8276
|
+
}
|
|
8277
|
+
processAllSections() {
|
|
8278
|
+
console.log('[FormComponent] Processing all sections - ONE TIME ONLY');
|
|
8279
|
+
this.processedSections = this.props.sections.map(section => ({
|
|
8280
|
+
name: section.name,
|
|
8281
|
+
fields: section.fields.map(field => this.processField(field))
|
|
8282
|
+
}));
|
|
8283
|
+
}
|
|
8284
|
+
processField(field) {
|
|
8285
|
+
// Generate token if not provided
|
|
8286
|
+
if (!field.token) {
|
|
8287
|
+
field.token = `input-${field.type}-${field.name}`;
|
|
8288
|
+
}
|
|
8289
|
+
// Debug: verificar opciones para select fields - SOLO UNA VEZ
|
|
8290
|
+
if (field.type === this.types.SEARCH_SELECT || field.type === this.types.MULTI_SELECT || field.type === this.types.MULTI_SELECT_SIMPLE) {
|
|
8291
|
+
console.log(`[FormComponent] Processing field ${field.name} options:`, {
|
|
8292
|
+
type: field.type,
|
|
8293
|
+
optionsCount: field.options?.length || 0,
|
|
8294
|
+
options: field.options?.slice(0, 2) || []
|
|
8295
|
+
});
|
|
8296
|
+
}
|
|
8297
|
+
if (field.type === this.types.NUMBER_FROM_TO) {
|
|
8298
|
+
const fromControl = this.getControl(`${field.name}_from`);
|
|
8299
|
+
const toControl = this.getControl(`${field.name}_to`);
|
|
8300
|
+
if (field.state === ComponentStates.DISABLED) {
|
|
8301
|
+
fromControl.disable();
|
|
8302
|
+
toControl.disable();
|
|
8303
|
+
}
|
|
8304
|
+
else {
|
|
8305
|
+
fromControl.enable();
|
|
8306
|
+
toControl.enable();
|
|
8307
|
+
}
|
|
8308
|
+
return {
|
|
8309
|
+
...field,
|
|
8310
|
+
fromControl,
|
|
8311
|
+
toControl,
|
|
8312
|
+
control: undefined, // Remove control for NUMBER_FROM_TO fields
|
|
8313
|
+
};
|
|
8314
|
+
}
|
|
8315
|
+
else {
|
|
8316
|
+
const control = this.getControl(field.name);
|
|
8317
|
+
if (field.state === ComponentStates.DISABLED) {
|
|
8318
|
+
control.disable();
|
|
8319
|
+
}
|
|
8320
|
+
else {
|
|
8321
|
+
control.enable();
|
|
8322
|
+
}
|
|
8323
|
+
return {
|
|
8324
|
+
...field,
|
|
8325
|
+
control,
|
|
8326
|
+
};
|
|
8327
|
+
}
|
|
8328
|
+
}
|
|
8329
|
+
setupChangeTracking() {
|
|
8345
8330
|
this.props.sections.forEach(section => {
|
|
8346
8331
|
section.fields
|
|
8347
8332
|
.filter(x => x.type === this.types.SELECT || x.type === this.types.TEXT || x.type === this.types.SEARCH_SELECT || x.type === this.types.MULTI_SELECT || x.type === this.types.MULTI_SELECT_SIMPLE)
|
|
@@ -8357,6 +8342,8 @@ class FormComponent {
|
|
|
8357
8342
|
}
|
|
8358
8343
|
trackSelectChanges(fieldName) {
|
|
8359
8344
|
const control = this.getControl(fieldName);
|
|
8345
|
+
// Initialize previous value
|
|
8346
|
+
this.previousValues.set(fieldName, control.value);
|
|
8360
8347
|
const subscription = control.valueChanges
|
|
8361
8348
|
.pipe(distinctUntilChanged$1()) // Evitar valores duplicados
|
|
8362
8349
|
.subscribe(value => {
|
|
@@ -8385,50 +8372,13 @@ class FormComponent {
|
|
|
8385
8372
|
getControl(field) {
|
|
8386
8373
|
return this.Form.get(field);
|
|
8387
8374
|
}
|
|
8375
|
+
/**
|
|
8376
|
+
* @deprecated This method is now only used internally.
|
|
8377
|
+
* Use processedSections property instead to avoid multiple calls.
|
|
8378
|
+
*/
|
|
8388
8379
|
getFieldProp(field) {
|
|
8389
|
-
|
|
8390
|
-
|
|
8391
|
-
field.token = `input-${field.type}-${field.name}`;
|
|
8392
|
-
}
|
|
8393
|
-
// Debug: verificar opciones para select fields
|
|
8394
|
-
if (field.type === this.types.SEARCH_SELECT || field.type === this.types.MULTI_SELECT || field.type === this.types.MULTI_SELECT_SIMPLE) {
|
|
8395
|
-
console.log(`[FormComponent] ${field.name} options:`, {
|
|
8396
|
-
type: field.type,
|
|
8397
|
-
optionsCount: field.options?.length || 0,
|
|
8398
|
-
options: field.options?.slice(0, 2) || []
|
|
8399
|
-
});
|
|
8400
|
-
}
|
|
8401
|
-
if (field.type === this.types.NUMBER_FROM_TO) {
|
|
8402
|
-
const fromControl = this.getControl(`${field.name}_from`);
|
|
8403
|
-
const toControl = this.getControl(`${field.name}_to`);
|
|
8404
|
-
if (field.state === ComponentStates.DISABLED) {
|
|
8405
|
-
fromControl.disable();
|
|
8406
|
-
toControl.disable();
|
|
8407
|
-
}
|
|
8408
|
-
else {
|
|
8409
|
-
fromControl.enable();
|
|
8410
|
-
toControl.enable();
|
|
8411
|
-
}
|
|
8412
|
-
return {
|
|
8413
|
-
...field,
|
|
8414
|
-
fromControl,
|
|
8415
|
-
toControl,
|
|
8416
|
-
control: undefined, // Remove control for NUMBER_FROM_TO fields
|
|
8417
|
-
};
|
|
8418
|
-
}
|
|
8419
|
-
else {
|
|
8420
|
-
const control = this.getControl(field.name);
|
|
8421
|
-
if (field.state === ComponentStates.DISABLED) {
|
|
8422
|
-
control.disable();
|
|
8423
|
-
}
|
|
8424
|
-
else {
|
|
8425
|
-
control.enable();
|
|
8426
|
-
}
|
|
8427
|
-
return {
|
|
8428
|
-
...field,
|
|
8429
|
-
control,
|
|
8430
|
-
};
|
|
8431
|
-
}
|
|
8380
|
+
console.warn('[FormComponent] getFieldProp is deprecated and should not be called directly');
|
|
8381
|
+
return this.processField(field);
|
|
8432
8382
|
}
|
|
8433
8383
|
get isAtEndOfForm() {
|
|
8434
8384
|
return isAtEnd(this.elementRef);
|
|
@@ -8442,6 +8392,19 @@ class FormComponent {
|
|
|
8442
8392
|
data: this.props,
|
|
8443
8393
|
};
|
|
8444
8394
|
}
|
|
8395
|
+
// Helper method to reset field value without triggering change event
|
|
8396
|
+
resetField(fieldName, emitEvent = false) {
|
|
8397
|
+
const control = this.getControl(fieldName);
|
|
8398
|
+
const currentValue = control.value;
|
|
8399
|
+
console.log(`[FormComponent] Resetting field ${fieldName}:`, { currentValue, emitEvent });
|
|
8400
|
+
// Update our cache to avoid triggering change event
|
|
8401
|
+
if (!emitEvent) {
|
|
8402
|
+
this.previousValues.set(fieldName, '');
|
|
8403
|
+
}
|
|
8404
|
+
control.setValue('', { emitEvent });
|
|
8405
|
+
control.markAsPristine();
|
|
8406
|
+
control.markAsUntouched();
|
|
8407
|
+
}
|
|
8445
8408
|
get actions() {
|
|
8446
8409
|
if (!this.form) {
|
|
8447
8410
|
return [];
|
|
@@ -8472,59 +8435,59 @@ class FormComponent {
|
|
|
8472
8435
|
size: 'large',
|
|
8473
8436
|
}"
|
|
8474
8437
|
></val-display>
|
|
8475
|
-
<div class="section" *ngFor="let s of
|
|
8438
|
+
<div class="section" *ngFor="let s of processedSections">
|
|
8476
8439
|
<val-title [props]="{ content: s.name, size: 'large', color: '', bold: false }"></val-title>
|
|
8477
8440
|
<div class="input" *ngFor="let f of s.fields">
|
|
8478
8441
|
<val-title [props]="{ content: f.label, size: 'small', color: 'dark', bold: false }"></val-title>
|
|
8479
8442
|
<ng-container *ngIf="f.type === types.TEXT">
|
|
8480
|
-
<val-text-input [props]="
|
|
8443
|
+
<val-text-input [props]="f"></val-text-input>
|
|
8481
8444
|
</ng-container>
|
|
8482
8445
|
<ng-container *ngIf="f.type === types.CHECK">
|
|
8483
8446
|
<val-check-input></val-check-input>
|
|
8484
8447
|
</ng-container>
|
|
8485
8448
|
<ng-container *ngIf="f.type === types.COMMENT">
|
|
8486
|
-
<val-comment-input [props]="
|
|
8449
|
+
<val-comment-input [props]="f"></val-comment-input>
|
|
8487
8450
|
</ng-container>
|
|
8488
8451
|
<ng-container *ngIf="f.type === types.DATE">
|
|
8489
|
-
<val-date-input [props]="
|
|
8452
|
+
<val-date-input [props]="f"></val-date-input>
|
|
8490
8453
|
</ng-container>
|
|
8491
8454
|
<ng-container *ngIf="f.type === types.EMAIL">
|
|
8492
|
-
<val-email-input [props]="
|
|
8455
|
+
<val-email-input [props]="f"></val-email-input>
|
|
8493
8456
|
</ng-container>
|
|
8494
8457
|
<ng-container *ngIf="f.type === types.FILE">
|
|
8495
|
-
<val-file-input [props]="
|
|
8458
|
+
<val-file-input [props]="f"></val-file-input>
|
|
8496
8459
|
</ng-container>
|
|
8497
8460
|
<ng-container *ngIf="f.type === types.HOUR">
|
|
8498
|
-
<val-hour-input [props]="
|
|
8461
|
+
<val-hour-input [props]="f"></val-hour-input>
|
|
8499
8462
|
</ng-container>
|
|
8500
8463
|
<ng-container *ngIf="f.type === types.NUMBER">
|
|
8501
|
-
<val-number-input [props]="
|
|
8464
|
+
<val-number-input [props]="f"></val-number-input>
|
|
8502
8465
|
</ng-container>
|
|
8503
8466
|
<ng-container *ngIf="f.type === types.NUMBER_FROM_TO">
|
|
8504
|
-
<val-number-from-to [props]="
|
|
8467
|
+
<val-number-from-to [props]="f"></val-number-from-to>
|
|
8505
8468
|
</ng-container>
|
|
8506
8469
|
<ng-container *ngIf="f.type === types.PASSWORD">
|
|
8507
|
-
<val-password-input [props]="
|
|
8470
|
+
<val-password-input [props]="f"></val-password-input>
|
|
8508
8471
|
</ng-container>
|
|
8509
8472
|
<ng-container *ngIf="f.type === types.PIN_CODE">
|
|
8510
|
-
<val-pin-input [props]="
|
|
8473
|
+
<val-pin-input [props]="f"></val-pin-input>
|
|
8511
8474
|
</ng-container>
|
|
8512
8475
|
<ng-container *ngIf="f.type === types.RADIO">
|
|
8513
|
-
<val-radio-input [props]="
|
|
8476
|
+
<val-radio-input [props]="f"></val-radio-input>
|
|
8514
8477
|
</ng-container>
|
|
8515
8478
|
<ng-container *ngIf="f.type === types.SELECT">
|
|
8516
|
-
<val-select-input [props]="
|
|
8479
|
+
<val-select-input [props]="f"></val-select-input>
|
|
8517
8480
|
</ng-container>
|
|
8518
8481
|
<ng-container *ngIf="f.type === types.SEARCH_SELECT">
|
|
8519
|
-
<val-select-search [props]="
|
|
8482
|
+
<val-select-search [props]="f"></val-select-search>
|
|
8520
8483
|
</ng-container>
|
|
8521
8484
|
<ng-container *ngIf="f.type === types.MULTI_SELECT">
|
|
8522
|
-
<val-multi-select-search [props]="
|
|
8485
|
+
<val-multi-select-search [props]="f"></val-multi-select-search>
|
|
8523
8486
|
</ng-container>
|
|
8524
8487
|
<ng-container *ngIf="f.type === types.MULTI_SELECT_SIMPLE">
|
|
8525
|
-
<val-multi-select-simple [props]="
|
|
8488
|
+
<val-multi-select-simple [props]="f"></val-multi-select-simple>
|
|
8526
8489
|
</ng-container>
|
|
8527
|
-
<val-hint [props]="
|
|
8490
|
+
<val-hint [props]="f"></val-hint>
|
|
8528
8491
|
</div>
|
|
8529
8492
|
<val-divider [props]="{ fill: 'solid', size: 'medium', color: 'medium' }"></val-divider>
|
|
8530
8493
|
<ng-content></ng-content>
|
|
@@ -8574,59 +8537,59 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
|
|
|
8574
8537
|
size: 'large',
|
|
8575
8538
|
}"
|
|
8576
8539
|
></val-display>
|
|
8577
|
-
<div class="section" *ngFor="let s of
|
|
8540
|
+
<div class="section" *ngFor="let s of processedSections">
|
|
8578
8541
|
<val-title [props]="{ content: s.name, size: 'large', color: '', bold: false }"></val-title>
|
|
8579
8542
|
<div class="input" *ngFor="let f of s.fields">
|
|
8580
8543
|
<val-title [props]="{ content: f.label, size: 'small', color: 'dark', bold: false }"></val-title>
|
|
8581
8544
|
<ng-container *ngIf="f.type === types.TEXT">
|
|
8582
|
-
<val-text-input [props]="
|
|
8545
|
+
<val-text-input [props]="f"></val-text-input>
|
|
8583
8546
|
</ng-container>
|
|
8584
8547
|
<ng-container *ngIf="f.type === types.CHECK">
|
|
8585
8548
|
<val-check-input></val-check-input>
|
|
8586
8549
|
</ng-container>
|
|
8587
8550
|
<ng-container *ngIf="f.type === types.COMMENT">
|
|
8588
|
-
<val-comment-input [props]="
|
|
8551
|
+
<val-comment-input [props]="f"></val-comment-input>
|
|
8589
8552
|
</ng-container>
|
|
8590
8553
|
<ng-container *ngIf="f.type === types.DATE">
|
|
8591
|
-
<val-date-input [props]="
|
|
8554
|
+
<val-date-input [props]="f"></val-date-input>
|
|
8592
8555
|
</ng-container>
|
|
8593
8556
|
<ng-container *ngIf="f.type === types.EMAIL">
|
|
8594
|
-
<val-email-input [props]="
|
|
8557
|
+
<val-email-input [props]="f"></val-email-input>
|
|
8595
8558
|
</ng-container>
|
|
8596
8559
|
<ng-container *ngIf="f.type === types.FILE">
|
|
8597
|
-
<val-file-input [props]="
|
|
8560
|
+
<val-file-input [props]="f"></val-file-input>
|
|
8598
8561
|
</ng-container>
|
|
8599
8562
|
<ng-container *ngIf="f.type === types.HOUR">
|
|
8600
|
-
<val-hour-input [props]="
|
|
8563
|
+
<val-hour-input [props]="f"></val-hour-input>
|
|
8601
8564
|
</ng-container>
|
|
8602
8565
|
<ng-container *ngIf="f.type === types.NUMBER">
|
|
8603
|
-
<val-number-input [props]="
|
|
8566
|
+
<val-number-input [props]="f"></val-number-input>
|
|
8604
8567
|
</ng-container>
|
|
8605
8568
|
<ng-container *ngIf="f.type === types.NUMBER_FROM_TO">
|
|
8606
|
-
<val-number-from-to [props]="
|
|
8569
|
+
<val-number-from-to [props]="f"></val-number-from-to>
|
|
8607
8570
|
</ng-container>
|
|
8608
8571
|
<ng-container *ngIf="f.type === types.PASSWORD">
|
|
8609
|
-
<val-password-input [props]="
|
|
8572
|
+
<val-password-input [props]="f"></val-password-input>
|
|
8610
8573
|
</ng-container>
|
|
8611
8574
|
<ng-container *ngIf="f.type === types.PIN_CODE">
|
|
8612
|
-
<val-pin-input [props]="
|
|
8575
|
+
<val-pin-input [props]="f"></val-pin-input>
|
|
8613
8576
|
</ng-container>
|
|
8614
8577
|
<ng-container *ngIf="f.type === types.RADIO">
|
|
8615
|
-
<val-radio-input [props]="
|
|
8578
|
+
<val-radio-input [props]="f"></val-radio-input>
|
|
8616
8579
|
</ng-container>
|
|
8617
8580
|
<ng-container *ngIf="f.type === types.SELECT">
|
|
8618
|
-
<val-select-input [props]="
|
|
8581
|
+
<val-select-input [props]="f"></val-select-input>
|
|
8619
8582
|
</ng-container>
|
|
8620
8583
|
<ng-container *ngIf="f.type === types.SEARCH_SELECT">
|
|
8621
|
-
<val-select-search [props]="
|
|
8584
|
+
<val-select-search [props]="f"></val-select-search>
|
|
8622
8585
|
</ng-container>
|
|
8623
8586
|
<ng-container *ngIf="f.type === types.MULTI_SELECT">
|
|
8624
|
-
<val-multi-select-search [props]="
|
|
8587
|
+
<val-multi-select-search [props]="f"></val-multi-select-search>
|
|
8625
8588
|
</ng-container>
|
|
8626
8589
|
<ng-container *ngIf="f.type === types.MULTI_SELECT_SIMPLE">
|
|
8627
|
-
<val-multi-select-simple [props]="
|
|
8590
|
+
<val-multi-select-simple [props]="f"></val-multi-select-simple>
|
|
8628
8591
|
</ng-container>
|
|
8629
|
-
<val-hint [props]="
|
|
8592
|
+
<val-hint [props]="f"></val-hint>
|
|
8630
8593
|
</div>
|
|
8631
8594
|
<val-divider [props]="{ fill: 'solid', size: 'medium', color: 'medium' }"></val-divider>
|
|
8632
8595
|
<ng-content></ng-content>
|