valtech-components 2.0.392 → 2.0.394
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 +90 -81
- package/esm2022/lib/components/molecules/select-search/select-search.component.mjs +83 -116
- package/esm2022/lib/components/organisms/form/form.component.mjs +27 -1
- package/fesm2022/valtech-components.mjs +196 -194
- package/fesm2022/valtech-components.mjs.map +1 -1
- package/lib/components/molecules/multi-select-simple/multi-select-simple.component.d.ts +9 -5
- 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 +1 -0
- 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,45 +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.displayValue =
|
|
5679
|
-
|
|
5680
|
-
if (selected.length === 0) {
|
|
5681
|
-
return '';
|
|
5682
|
-
}
|
|
5683
|
-
if (selected.length === 1) {
|
|
5684
|
-
const option = this.getOptionByValue(selected[0]);
|
|
5685
|
-
return option ? option[this.labelProperty] : '';
|
|
5686
|
-
}
|
|
5687
|
-
return `${selected.length} elementos seleccionados`;
|
|
5688
|
-
});
|
|
5689
|
-
this.filteredOptions = computed(() => {
|
|
5690
|
-
const options = this.props?.options || [];
|
|
5691
|
-
const search = this.searchTerm().toLowerCase();
|
|
5692
|
-
// Debug
|
|
5693
|
-
console.log('[MultiSelectSimple] filteredOptions computed:', {
|
|
5694
|
-
optionsCount: options.length,
|
|
5695
|
-
searchTerm: search,
|
|
5696
|
-
options: options.slice(0, 3) // Primeras 3 opciones
|
|
5697
|
-
});
|
|
5698
|
-
if (!search) {
|
|
5699
|
-
return options;
|
|
5700
|
-
}
|
|
5701
|
-
return options.filter(option => {
|
|
5702
|
-
const label = option[this.labelProperty]
|
|
5703
|
-
? replaceSpecialChars(String(option[this.labelProperty]).toLowerCase())
|
|
5704
|
-
: '';
|
|
5705
|
-
const value = option[this.valueProperty]
|
|
5706
|
-
? replaceSpecialChars(String(option[this.valueProperty]).toLowerCase())
|
|
5707
|
-
: '';
|
|
5708
|
-
const searchTerm = replaceSpecialChars(search);
|
|
5709
|
-
return label.includes(searchTerm) || value.includes(searchTerm);
|
|
5710
|
-
});
|
|
5711
|
-
});
|
|
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 = [];
|
|
5712
5680
|
this.searchPlaceholder = '';
|
|
5713
5681
|
// ControlValueAccessor
|
|
5714
5682
|
this.onChange = (_value) => { };
|
|
@@ -5720,20 +5688,26 @@ class MultiSelectSimpleComponent {
|
|
|
5720
5688
|
}
|
|
5721
5689
|
ngOnInit() {
|
|
5722
5690
|
this.applyDefaultValue();
|
|
5691
|
+
this.initializeOptions();
|
|
5723
5692
|
this.syncSelectedValues();
|
|
5693
|
+
this.updateDisplayValue();
|
|
5724
5694
|
}
|
|
5725
5695
|
ngOnDestroy() {
|
|
5726
5696
|
document.removeEventListener('click', this.handleClickOutside.bind(this));
|
|
5727
5697
|
}
|
|
5728
5698
|
ngOnChanges(changes) {
|
|
5729
5699
|
if (changes['props'] && this.props) {
|
|
5700
|
+
this.initializeOptions();
|
|
5730
5701
|
this.syncSelectedValues();
|
|
5702
|
+
this.updateDisplayValue();
|
|
5703
|
+
this.changeDetector.detectChanges();
|
|
5731
5704
|
}
|
|
5732
5705
|
}
|
|
5733
5706
|
// ControlValueAccessor implementation
|
|
5734
5707
|
writeValue(value) {
|
|
5735
5708
|
const valueArray = this.parseValue(value);
|
|
5736
|
-
this.selectedValues
|
|
5709
|
+
this.selectedValues = valueArray;
|
|
5710
|
+
this.updateDisplayValue();
|
|
5737
5711
|
}
|
|
5738
5712
|
registerOnChange(fn) {
|
|
5739
5713
|
this.onChange = fn;
|
|
@@ -5747,8 +5721,8 @@ class MultiSelectSimpleComponent {
|
|
|
5747
5721
|
// Component methods
|
|
5748
5722
|
toggleDropdown(event) {
|
|
5749
5723
|
event.stopPropagation();
|
|
5750
|
-
this.isOpen
|
|
5751
|
-
if (this.isOpen
|
|
5724
|
+
this.isOpen = !this.isOpen;
|
|
5725
|
+
if (this.isOpen) {
|
|
5752
5726
|
this.onTouched();
|
|
5753
5727
|
// Focus search bar when opening
|
|
5754
5728
|
setTimeout(() => {
|
|
@@ -5760,45 +5734,48 @@ class MultiSelectSimpleComponent {
|
|
|
5760
5734
|
}
|
|
5761
5735
|
}
|
|
5762
5736
|
onSearch(event) {
|
|
5763
|
-
this.searchTerm
|
|
5737
|
+
this.searchTerm = event.detail.value || '';
|
|
5738
|
+
this.filterOptions();
|
|
5764
5739
|
}
|
|
5765
5740
|
toggleOption(option) {
|
|
5766
|
-
const value = String(option[this.valueProperty]);
|
|
5767
|
-
const currentSelected = this.selectedValues();
|
|
5741
|
+
const value = String(option[this.valueProperty]);
|
|
5768
5742
|
if (this.isSelected(option)) {
|
|
5769
5743
|
// Remove from selection
|
|
5770
|
-
this.selectedValues.
|
|
5744
|
+
this.selectedValues = this.selectedValues.filter(v => v !== value);
|
|
5771
5745
|
}
|
|
5772
5746
|
else {
|
|
5773
5747
|
// Add to selection
|
|
5774
|
-
this.selectedValues
|
|
5748
|
+
this.selectedValues = [...this.selectedValues, value];
|
|
5775
5749
|
}
|
|
5750
|
+
this.updateDisplayValue();
|
|
5776
5751
|
this.emitValue();
|
|
5777
5752
|
}
|
|
5778
5753
|
selectAll() {
|
|
5779
|
-
const allValues = this.filteredOptions
|
|
5780
|
-
const currentSelected = this.selectedValues();
|
|
5754
|
+
const allValues = this.filteredOptions.map(option => String(option[this.valueProperty]));
|
|
5781
5755
|
// Add only new values to avoid duplicates
|
|
5782
|
-
const newValues = allValues.filter(value => !
|
|
5783
|
-
this.selectedValues
|
|
5756
|
+
const newValues = allValues.filter(value => !this.selectedValues.includes(value));
|
|
5757
|
+
this.selectedValues = [...this.selectedValues, ...newValues];
|
|
5758
|
+
this.updateDisplayValue();
|
|
5784
5759
|
this.emitValue();
|
|
5785
5760
|
}
|
|
5786
5761
|
clearAll() {
|
|
5787
|
-
this.selectedValues
|
|
5762
|
+
this.selectedValues = [];
|
|
5763
|
+
this.updateDisplayValue();
|
|
5788
5764
|
this.emitValue();
|
|
5789
5765
|
}
|
|
5790
5766
|
isSelected(option) {
|
|
5791
|
-
return this.selectedValues
|
|
5767
|
+
return this.selectedValues.includes(String(option[this.valueProperty]));
|
|
5792
5768
|
}
|
|
5793
5769
|
trackByFn(_index, option) {
|
|
5794
5770
|
return option[this.valueProperty];
|
|
5795
5771
|
}
|
|
5796
5772
|
handleClickOutside(event) {
|
|
5797
|
-
if (this.isOpen
|
|
5773
|
+
if (this.isOpen &&
|
|
5798
5774
|
!this.mainInputRef?.nativeElement?.contains(event.target) &&
|
|
5799
5775
|
!this.dropdownRef?.nativeElement?.contains(event.target)) {
|
|
5800
|
-
this.isOpen
|
|
5801
|
-
this.searchTerm
|
|
5776
|
+
this.isOpen = false;
|
|
5777
|
+
this.searchTerm = '';
|
|
5778
|
+
this.initializeOptions();
|
|
5802
5779
|
}
|
|
5803
5780
|
}
|
|
5804
5781
|
parseValue(value) {
|
|
@@ -5812,7 +5789,7 @@ class MultiSelectSimpleComponent {
|
|
|
5812
5789
|
return [String(value)];
|
|
5813
5790
|
}
|
|
5814
5791
|
emitValue() {
|
|
5815
|
-
const value = this.selectedValues
|
|
5792
|
+
const value = this.selectedValues.join(',');
|
|
5816
5793
|
this.onChange(value);
|
|
5817
5794
|
if (this.props?.control) {
|
|
5818
5795
|
this.props.control.setValue(value);
|
|
@@ -5821,17 +5798,49 @@ class MultiSelectSimpleComponent {
|
|
|
5821
5798
|
}
|
|
5822
5799
|
}
|
|
5823
5800
|
getOptionByValue(value) {
|
|
5824
|
-
return this.
|
|
5801
|
+
return this.filteredOptions.find(option => String(option[this.valueProperty]) === String(value));
|
|
5825
5802
|
}
|
|
5826
5803
|
applyDefaultValue() {
|
|
5827
5804
|
if (this.props) {
|
|
5828
5805
|
applyDefaultValueToControl(this.props);
|
|
5829
5806
|
}
|
|
5830
5807
|
}
|
|
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);
|
|
5825
|
+
});
|
|
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
|
+
}
|
|
5831
5840
|
syncSelectedValues() {
|
|
5832
5841
|
if (this.props?.control?.value) {
|
|
5833
5842
|
const valueArray = this.parseValue(this.props.control.value);
|
|
5834
|
-
this.selectedValues
|
|
5843
|
+
this.selectedValues = valueArray;
|
|
5835
5844
|
}
|
|
5836
5845
|
}
|
|
5837
5846
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: MultiSelectSimpleComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
@@ -5847,18 +5856,18 @@ class MultiSelectSimpleComponent {
|
|
|
5847
5856
|
<ion-input
|
|
5848
5857
|
#mainInput
|
|
5849
5858
|
type="text"
|
|
5850
|
-
[value]="displayValue
|
|
5859
|
+
[value]="displayValue"
|
|
5851
5860
|
[placeholder]="props?.placeholder || placeholder"
|
|
5852
5861
|
readonly
|
|
5853
5862
|
class="main-input"
|
|
5854
|
-
[class.is-open]="isOpen
|
|
5863
|
+
[class.is-open]="isOpen"
|
|
5855
5864
|
/>
|
|
5856
5865
|
|
|
5857
5866
|
<!-- Dropdown icon -->
|
|
5858
5867
|
<ion-icon
|
|
5859
5868
|
name="chevron-down-outline"
|
|
5860
5869
|
class="dropdown-icon"
|
|
5861
|
-
[class.rotated]="isOpen
|
|
5870
|
+
[class.rotated]="isOpen"
|
|
5862
5871
|
></ion-icon>
|
|
5863
5872
|
|
|
5864
5873
|
<!-- Hidden input for form control -->
|
|
@@ -5872,7 +5881,7 @@ class MultiSelectSimpleComponent {
|
|
|
5872
5881
|
<!-- Dropdown overlay -->
|
|
5873
5882
|
<div
|
|
5874
5883
|
class="dropdown-overlay"
|
|
5875
|
-
[class.visible]="isOpen
|
|
5884
|
+
[class.visible]="isOpen"
|
|
5876
5885
|
#dropdown
|
|
5877
5886
|
>
|
|
5878
5887
|
<!-- Search bar -->
|
|
@@ -5881,7 +5890,7 @@ class MultiSelectSimpleComponent {
|
|
|
5881
5890
|
#searchbar
|
|
5882
5891
|
[placeholder]="searchPlaceholder"
|
|
5883
5892
|
(ionInput)="onSearch($event)"
|
|
5884
|
-
[value]="searchTerm
|
|
5893
|
+
[value]="searchTerm"
|
|
5885
5894
|
show-clear-button="focus"
|
|
5886
5895
|
></ion-searchbar>
|
|
5887
5896
|
</div>
|
|
@@ -5892,7 +5901,7 @@ class MultiSelectSimpleComponent {
|
|
|
5892
5901
|
fill="clear"
|
|
5893
5902
|
size="small"
|
|
5894
5903
|
(click)="selectAll()"
|
|
5895
|
-
[disabled]="filteredOptions
|
|
5904
|
+
[disabled]="filteredOptions.length === 0"
|
|
5896
5905
|
>
|
|
5897
5906
|
Seleccionar todos
|
|
5898
5907
|
</ion-button>
|
|
@@ -5901,7 +5910,7 @@ class MultiSelectSimpleComponent {
|
|
|
5901
5910
|
size="small"
|
|
5902
5911
|
color="medium"
|
|
5903
5912
|
(click)="clearAll()"
|
|
5904
|
-
[disabled]="selectedValues
|
|
5913
|
+
[disabled]="selectedValues.length === 0"
|
|
5905
5914
|
>
|
|
5906
5915
|
Limpiar
|
|
5907
5916
|
</ion-button>
|
|
@@ -5911,7 +5920,7 @@ class MultiSelectSimpleComponent {
|
|
|
5911
5920
|
<div class="options-container">
|
|
5912
5921
|
<ion-list class="options-list">
|
|
5913
5922
|
<ion-item
|
|
5914
|
-
*ngFor="let option of filteredOptions
|
|
5923
|
+
*ngFor="let option of filteredOptions; trackBy: trackByFn"
|
|
5915
5924
|
button
|
|
5916
5925
|
(click)="toggleOption(option)"
|
|
5917
5926
|
class="option-item"
|
|
@@ -5924,9 +5933,9 @@ class MultiSelectSimpleComponent {
|
|
|
5924
5933
|
</ion-item>
|
|
5925
5934
|
|
|
5926
5935
|
<!-- No results message -->
|
|
5927
|
-
<ion-item *ngIf="filteredOptions
|
|
5936
|
+
<ion-item *ngIf="filteredOptions.length === 0" class="no-results">
|
|
5928
5937
|
<ion-label color="medium">
|
|
5929
|
-
{{ searchTerm
|
|
5938
|
+
{{ searchTerm ? 'No se encontraron resultados' : 'No hay opciones disponibles' }}
|
|
5930
5939
|
</ion-label>
|
|
5931
5940
|
</ion-item>
|
|
5932
5941
|
</ion-list>
|
|
@@ -5948,18 +5957,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
|
|
|
5948
5957
|
<ion-input
|
|
5949
5958
|
#mainInput
|
|
5950
5959
|
type="text"
|
|
5951
|
-
[value]="displayValue
|
|
5960
|
+
[value]="displayValue"
|
|
5952
5961
|
[placeholder]="props?.placeholder || placeholder"
|
|
5953
5962
|
readonly
|
|
5954
5963
|
class="main-input"
|
|
5955
|
-
[class.is-open]="isOpen
|
|
5964
|
+
[class.is-open]="isOpen"
|
|
5956
5965
|
/>
|
|
5957
5966
|
|
|
5958
5967
|
<!-- Dropdown icon -->
|
|
5959
5968
|
<ion-icon
|
|
5960
5969
|
name="chevron-down-outline"
|
|
5961
5970
|
class="dropdown-icon"
|
|
5962
|
-
[class.rotated]="isOpen
|
|
5971
|
+
[class.rotated]="isOpen"
|
|
5963
5972
|
></ion-icon>
|
|
5964
5973
|
|
|
5965
5974
|
<!-- Hidden input for form control -->
|
|
@@ -5973,7 +5982,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
|
|
|
5973
5982
|
<!-- Dropdown overlay -->
|
|
5974
5983
|
<div
|
|
5975
5984
|
class="dropdown-overlay"
|
|
5976
|
-
[class.visible]="isOpen
|
|
5985
|
+
[class.visible]="isOpen"
|
|
5977
5986
|
#dropdown
|
|
5978
5987
|
>
|
|
5979
5988
|
<!-- Search bar -->
|
|
@@ -5982,7 +5991,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
|
|
|
5982
5991
|
#searchbar
|
|
5983
5992
|
[placeholder]="searchPlaceholder"
|
|
5984
5993
|
(ionInput)="onSearch($event)"
|
|
5985
|
-
[value]="searchTerm
|
|
5994
|
+
[value]="searchTerm"
|
|
5986
5995
|
show-clear-button="focus"
|
|
5987
5996
|
></ion-searchbar>
|
|
5988
5997
|
</div>
|
|
@@ -5993,7 +6002,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
|
|
|
5993
6002
|
fill="clear"
|
|
5994
6003
|
size="small"
|
|
5995
6004
|
(click)="selectAll()"
|
|
5996
|
-
[disabled]="filteredOptions
|
|
6005
|
+
[disabled]="filteredOptions.length === 0"
|
|
5997
6006
|
>
|
|
5998
6007
|
Seleccionar todos
|
|
5999
6008
|
</ion-button>
|
|
@@ -6002,7 +6011,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
|
|
|
6002
6011
|
size="small"
|
|
6003
6012
|
color="medium"
|
|
6004
6013
|
(click)="clearAll()"
|
|
6005
|
-
[disabled]="selectedValues
|
|
6014
|
+
[disabled]="selectedValues.length === 0"
|
|
6006
6015
|
>
|
|
6007
6016
|
Limpiar
|
|
6008
6017
|
</ion-button>
|
|
@@ -6012,7 +6021,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
|
|
|
6012
6021
|
<div class="options-container">
|
|
6013
6022
|
<ion-list class="options-list">
|
|
6014
6023
|
<ion-item
|
|
6015
|
-
*ngFor="let option of filteredOptions
|
|
6024
|
+
*ngFor="let option of filteredOptions; trackBy: trackByFn"
|
|
6016
6025
|
button
|
|
6017
6026
|
(click)="toggleOption(option)"
|
|
6018
6027
|
class="option-item"
|
|
@@ -6025,9 +6034,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
|
|
|
6025
6034
|
</ion-item>
|
|
6026
6035
|
|
|
6027
6036
|
<!-- No results message -->
|
|
6028
|
-
<ion-item *ngIf="filteredOptions
|
|
6037
|
+
<ion-item *ngIf="filteredOptions.length === 0" class="no-results">
|
|
6029
6038
|
<ion-label color="medium">
|
|
6030
|
-
{{ searchTerm
|
|
6039
|
+
{{ searchTerm ? 'No se encontraron resultados' : 'No hay opciones disponibles' }}
|
|
6031
6040
|
</ion-label>
|
|
6032
6041
|
</ion-item>
|
|
6033
6042
|
</ion-list>
|
|
@@ -6765,105 +6774,39 @@ class SelectSearchComponent {
|
|
|
6765
6774
|
this.valueProperty = 'id';
|
|
6766
6775
|
this.placeholder = '';
|
|
6767
6776
|
this.langService = inject(LangService);
|
|
6768
|
-
|
|
6769
|
-
|
|
6770
|
-
this.
|
|
6771
|
-
this.
|
|
6772
|
-
|
|
6773
|
-
this.
|
|
6774
|
-
|
|
6775
|
-
this.displayValue = computed(() => {
|
|
6776
|
-
const value = this.selectedValue();
|
|
6777
|
-
if (!value)
|
|
6778
|
-
return '';
|
|
6779
|
-
const option = this.getOptionByValue(value);
|
|
6780
|
-
return option ? option[this.labelProperty] : '';
|
|
6781
|
-
});
|
|
6782
|
-
this.filteredOptions = computed(() => {
|
|
6783
|
-
// Use signal instead of props directly
|
|
6784
|
-
const options = this.optionsSignal();
|
|
6785
|
-
const search = this.searchTerm().toLowerCase();
|
|
6786
|
-
// Debug mejorado
|
|
6787
|
-
console.log('[SelectSearch] filteredOptions computed:', {
|
|
6788
|
-
optionsCount: options.length,
|
|
6789
|
-
searchTerm: search,
|
|
6790
|
-
options: options,
|
|
6791
|
-
labelProperty: this.labelProperty,
|
|
6792
|
-
valueProperty: this.valueProperty,
|
|
6793
|
-
propsExists: !!this.props,
|
|
6794
|
-
propsOptionsExists: !!this.props?.options,
|
|
6795
|
-
propsOptionsLength: this.props?.options?.length || 0,
|
|
6796
|
-
firstOptionStructure: options[0] ? {
|
|
6797
|
-
[this.labelProperty]: options[0][this.labelProperty],
|
|
6798
|
-
[this.valueProperty]: options[0][this.valueProperty],
|
|
6799
|
-
typeOfId: typeof options[0][this.valueProperty],
|
|
6800
|
-
fullOption: options[0]
|
|
6801
|
-
} : 'NO_OPTIONS'
|
|
6802
|
-
});
|
|
6803
|
-
if (!search) {
|
|
6804
|
-
return options;
|
|
6805
|
-
}
|
|
6806
|
-
return options.filter(option => {
|
|
6807
|
-
const label = option[this.labelProperty]
|
|
6808
|
-
? replaceSpecialChars(String(option[this.labelProperty]).toLowerCase())
|
|
6809
|
-
: '';
|
|
6810
|
-
const value = option[this.valueProperty]
|
|
6811
|
-
? replaceSpecialChars(String(option[this.valueProperty]).toLowerCase())
|
|
6812
|
-
: '';
|
|
6813
|
-
const searchTerm = replaceSpecialChars(search);
|
|
6814
|
-
return label.includes(searchTerm) || value.includes(searchTerm);
|
|
6815
|
-
});
|
|
6816
|
-
});
|
|
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 = [];
|
|
6817
6784
|
this.placeholder = this.langService.getText('_global', 'selectOption', 'Seleccione una opción');
|
|
6818
6785
|
// Close dropdown when clicking outside
|
|
6819
6786
|
document.addEventListener('click', this.handleClickOutside.bind(this));
|
|
6820
|
-
// Debug effect to track when filteredOptions changes
|
|
6821
|
-
effect(() => {
|
|
6822
|
-
const options = this.filteredOptions();
|
|
6823
|
-
console.log('[SelectSearch] EFFECT - filteredOptions changed:', {
|
|
6824
|
-
optionsCount: options.length,
|
|
6825
|
-
options: options,
|
|
6826
|
-
timestamp: new Date().toLocaleTimeString()
|
|
6827
|
-
});
|
|
6828
|
-
});
|
|
6829
6787
|
}
|
|
6830
6788
|
ngOnInit() {
|
|
6831
|
-
console.log('[SelectSearch] ngOnInit:', {
|
|
6832
|
-
props: !!this.props,
|
|
6833
|
-
options: this.props?.options?.length || 0,
|
|
6834
|
-
control: !!this.props?.control
|
|
6835
|
-
});
|
|
6836
|
-
// Update options signal
|
|
6837
|
-
if (this.props?.options) {
|
|
6838
|
-
this.optionsSignal.set(this.props.options);
|
|
6839
|
-
}
|
|
6840
6789
|
this.applyDefaultValue();
|
|
6790
|
+
this.initializeOptions();
|
|
6841
6791
|
this.syncSelectedValue();
|
|
6792
|
+
this.updateDisplayValue();
|
|
6842
6793
|
}
|
|
6843
6794
|
ngOnDestroy() {
|
|
6844
6795
|
document.removeEventListener('click', this.handleClickOutside.bind(this));
|
|
6845
6796
|
}
|
|
6846
6797
|
ngOnChanges(changes) {
|
|
6847
|
-
console.log('[SelectSearch] ngOnChanges:', {
|
|
6848
|
-
hasPropsChange: !!changes['props'],
|
|
6849
|
-
props: !!this.props,
|
|
6850
|
-
options: this.props?.options?.length || 0,
|
|
6851
|
-
propsOptions: this.props?.options
|
|
6852
|
-
});
|
|
6853
6798
|
if (changes['props'] && this.props) {
|
|
6854
|
-
|
|
6855
|
-
if (this.props.options) {
|
|
6856
|
-
console.log('[SelectSearch] Updating optionsSignal with:', this.props.options);
|
|
6857
|
-
this.optionsSignal.set(this.props.options);
|
|
6858
|
-
}
|
|
6799
|
+
this.initializeOptions();
|
|
6859
6800
|
this.syncSelectedValue();
|
|
6801
|
+
this.updateDisplayValue();
|
|
6802
|
+
this.changeDetector.detectChanges();
|
|
6860
6803
|
}
|
|
6861
6804
|
}
|
|
6862
6805
|
// Component methods
|
|
6863
6806
|
toggleDropdown(event) {
|
|
6864
6807
|
event.stopPropagation();
|
|
6865
|
-
this.isOpen
|
|
6866
|
-
if (this.isOpen
|
|
6808
|
+
this.isOpen = !this.isOpen;
|
|
6809
|
+
if (this.isOpen) {
|
|
6867
6810
|
// Focus search bar when opening if available
|
|
6868
6811
|
setTimeout(() => {
|
|
6869
6812
|
const searchbar = this.dropdownRef?.nativeElement?.querySelector('ion-searchbar');
|
|
@@ -6874,13 +6817,16 @@ class SelectSearchComponent {
|
|
|
6874
6817
|
}
|
|
6875
6818
|
}
|
|
6876
6819
|
onSearch(event) {
|
|
6877
|
-
this.searchTerm
|
|
6820
|
+
this.searchTerm = event.detail.value || '';
|
|
6821
|
+
this.filterOptions();
|
|
6878
6822
|
}
|
|
6879
6823
|
selectOption(option) {
|
|
6880
|
-
const value = String(option[this.valueProperty]);
|
|
6881
|
-
this.selectedValue
|
|
6882
|
-
this.isOpen
|
|
6883
|
-
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
|
|
6884
6830
|
// Update form control
|
|
6885
6831
|
if (this.props?.control) {
|
|
6886
6832
|
this.props.control.setValue(value);
|
|
@@ -6889,21 +6835,49 @@ class SelectSearchComponent {
|
|
|
6889
6835
|
}
|
|
6890
6836
|
}
|
|
6891
6837
|
isSelected(option) {
|
|
6892
|
-
return String(this.selectedValue
|
|
6838
|
+
return String(this.selectedValue) === String(option[this.valueProperty]);
|
|
6893
6839
|
}
|
|
6894
6840
|
trackByFn(_index, option) {
|
|
6895
6841
|
return option[this.valueProperty];
|
|
6896
6842
|
}
|
|
6897
6843
|
handleClickOutside(event) {
|
|
6898
|
-
if (this.isOpen
|
|
6844
|
+
if (this.isOpen &&
|
|
6899
6845
|
!this.mainInputRef?.nativeElement?.contains(event.target) &&
|
|
6900
6846
|
!this.dropdownRef?.nativeElement?.contains(event.target)) {
|
|
6901
|
-
this.isOpen
|
|
6902
|
-
this.searchTerm
|
|
6847
|
+
this.isOpen = false;
|
|
6848
|
+
this.searchTerm = '';
|
|
6849
|
+
this.initializeOptions();
|
|
6903
6850
|
}
|
|
6904
6851
|
}
|
|
6905
6852
|
getOptionByValue(value) {
|
|
6906
|
-
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] : '';
|
|
6907
6881
|
}
|
|
6908
6882
|
applyDefaultValue() {
|
|
6909
6883
|
if (this.props) {
|
|
@@ -6913,15 +6887,17 @@ class SelectSearchComponent {
|
|
|
6913
6887
|
debugOptions() {
|
|
6914
6888
|
console.log('[SelectSearch] DEBUG CLICK:', {
|
|
6915
6889
|
props: this.props,
|
|
6916
|
-
filteredOptions: this.filteredOptions
|
|
6917
|
-
searchTerm: this.searchTerm
|
|
6890
|
+
filteredOptions: this.filteredOptions,
|
|
6891
|
+
searchTerm: this.searchTerm,
|
|
6918
6892
|
propsOptions: this.props?.options,
|
|
6919
|
-
|
|
6893
|
+
displayValue: this.displayValue,
|
|
6894
|
+
selectedValue: this.selectedValue,
|
|
6895
|
+
isOpen: this.isOpen
|
|
6920
6896
|
});
|
|
6921
6897
|
}
|
|
6922
6898
|
syncSelectedValue() {
|
|
6923
6899
|
if (this.props?.control?.value) {
|
|
6924
|
-
this.selectedValue
|
|
6900
|
+
this.selectedValue = String(this.props.control.value);
|
|
6925
6901
|
}
|
|
6926
6902
|
}
|
|
6927
6903
|
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: SelectSearchComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
@@ -6931,18 +6907,18 @@ class SelectSearchComponent {
|
|
|
6931
6907
|
<ion-input
|
|
6932
6908
|
#mainInput
|
|
6933
6909
|
type="text"
|
|
6934
|
-
[value]="displayValue
|
|
6910
|
+
[value]="displayValue"
|
|
6935
6911
|
[placeholder]="props?.placeholder || placeholder"
|
|
6936
6912
|
readonly
|
|
6937
6913
|
class="main-input"
|
|
6938
|
-
[class.is-open]="isOpen
|
|
6914
|
+
[class.is-open]="isOpen"
|
|
6939
6915
|
/>
|
|
6940
6916
|
|
|
6941
6917
|
<!-- Dropdown icon -->
|
|
6942
6918
|
<ion-icon
|
|
6943
6919
|
name="chevron-down-outline"
|
|
6944
6920
|
class="dropdown-icon"
|
|
6945
|
-
[class.rotated]="isOpen
|
|
6921
|
+
[class.rotated]="isOpen"
|
|
6946
6922
|
></ion-icon>
|
|
6947
6923
|
|
|
6948
6924
|
<!-- Hidden input for form control -->
|
|
@@ -6956,12 +6932,12 @@ class SelectSearchComponent {
|
|
|
6956
6932
|
<!-- Dropdown overlay -->
|
|
6957
6933
|
<div
|
|
6958
6934
|
class="dropdown-overlay"
|
|
6959
|
-
[class.visible]="isOpen
|
|
6935
|
+
[class.visible]="isOpen"
|
|
6960
6936
|
#dropdown
|
|
6961
6937
|
>
|
|
6962
6938
|
<!-- Debug button -->
|
|
6963
6939
|
<div class="search-container" style="background: red; color: white; padding: 4px;">
|
|
6964
|
-
<button (click)="debugOptions()">DEBUG: {{ filteredOptions
|
|
6940
|
+
<button (click)="debugOptions()">DEBUG: {{ filteredOptions.length }} opciones</button>
|
|
6965
6941
|
</div>
|
|
6966
6942
|
|
|
6967
6943
|
<!-- Search bar -->
|
|
@@ -6970,7 +6946,7 @@ class SelectSearchComponent {
|
|
|
6970
6946
|
#searchbar
|
|
6971
6947
|
[placeholder]="'Buscar'"
|
|
6972
6948
|
(ionInput)="onSearch($event)"
|
|
6973
|
-
[value]="searchTerm
|
|
6949
|
+
[value]="searchTerm"
|
|
6974
6950
|
show-clear-button="focus"
|
|
6975
6951
|
[debounce]="200"
|
|
6976
6952
|
></ion-searchbar>
|
|
@@ -6980,7 +6956,7 @@ class SelectSearchComponent {
|
|
|
6980
6956
|
<div class="options-container">
|
|
6981
6957
|
<ion-list class="options-list">
|
|
6982
6958
|
<ion-item
|
|
6983
|
-
*ngFor="let option of filteredOptions
|
|
6959
|
+
*ngFor="let option of filteredOptions; trackBy: trackByFn"
|
|
6984
6960
|
button
|
|
6985
6961
|
(click)="selectOption(option)"
|
|
6986
6962
|
class="option-item"
|
|
@@ -6995,11 +6971,11 @@ class SelectSearchComponent {
|
|
|
6995
6971
|
</ion-item>
|
|
6996
6972
|
|
|
6997
6973
|
<!-- No results message -->
|
|
6998
|
-
<ion-item *ngIf="filteredOptions
|
|
6974
|
+
<ion-item *ngIf="filteredOptions.length === 0" class="no-results">
|
|
6999
6975
|
<ion-label color="medium">
|
|
7000
|
-
{{ searchTerm
|
|
6976
|
+
{{ searchTerm ? 'No se encontraron resultados' : 'No hay opciones disponibles' }}
|
|
7001
6977
|
<!-- Debug info -->
|
|
7002
|
-
<br><small>Debug: {{ filteredOptions
|
|
6978
|
+
<br><small>Debug: {{ filteredOptions.length }} opciones | Props: {{ !!props }} | Search: "{{ searchTerm }}"</small>
|
|
7003
6979
|
</ion-label>
|
|
7004
6980
|
</ion-item>
|
|
7005
6981
|
</ion-list>
|
|
@@ -7015,18 +6991,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
|
|
|
7015
6991
|
<ion-input
|
|
7016
6992
|
#mainInput
|
|
7017
6993
|
type="text"
|
|
7018
|
-
[value]="displayValue
|
|
6994
|
+
[value]="displayValue"
|
|
7019
6995
|
[placeholder]="props?.placeholder || placeholder"
|
|
7020
6996
|
readonly
|
|
7021
6997
|
class="main-input"
|
|
7022
|
-
[class.is-open]="isOpen
|
|
6998
|
+
[class.is-open]="isOpen"
|
|
7023
6999
|
/>
|
|
7024
7000
|
|
|
7025
7001
|
<!-- Dropdown icon -->
|
|
7026
7002
|
<ion-icon
|
|
7027
7003
|
name="chevron-down-outline"
|
|
7028
7004
|
class="dropdown-icon"
|
|
7029
|
-
[class.rotated]="isOpen
|
|
7005
|
+
[class.rotated]="isOpen"
|
|
7030
7006
|
></ion-icon>
|
|
7031
7007
|
|
|
7032
7008
|
<!-- Hidden input for form control -->
|
|
@@ -7040,12 +7016,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
|
|
|
7040
7016
|
<!-- Dropdown overlay -->
|
|
7041
7017
|
<div
|
|
7042
7018
|
class="dropdown-overlay"
|
|
7043
|
-
[class.visible]="isOpen
|
|
7019
|
+
[class.visible]="isOpen"
|
|
7044
7020
|
#dropdown
|
|
7045
7021
|
>
|
|
7046
7022
|
<!-- Debug button -->
|
|
7047
7023
|
<div class="search-container" style="background: red; color: white; padding: 4px;">
|
|
7048
|
-
<button (click)="debugOptions()">DEBUG: {{ filteredOptions
|
|
7024
|
+
<button (click)="debugOptions()">DEBUG: {{ filteredOptions.length }} opciones</button>
|
|
7049
7025
|
</div>
|
|
7050
7026
|
|
|
7051
7027
|
<!-- Search bar -->
|
|
@@ -7054,7 +7030,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
|
|
|
7054
7030
|
#searchbar
|
|
7055
7031
|
[placeholder]="'Buscar'"
|
|
7056
7032
|
(ionInput)="onSearch($event)"
|
|
7057
|
-
[value]="searchTerm
|
|
7033
|
+
[value]="searchTerm"
|
|
7058
7034
|
show-clear-button="focus"
|
|
7059
7035
|
[debounce]="200"
|
|
7060
7036
|
></ion-searchbar>
|
|
@@ -7064,7 +7040,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
|
|
|
7064
7040
|
<div class="options-container">
|
|
7065
7041
|
<ion-list class="options-list">
|
|
7066
7042
|
<ion-item
|
|
7067
|
-
*ngFor="let option of filteredOptions
|
|
7043
|
+
*ngFor="let option of filteredOptions; trackBy: trackByFn"
|
|
7068
7044
|
button
|
|
7069
7045
|
(click)="selectOption(option)"
|
|
7070
7046
|
class="option-item"
|
|
@@ -7079,11 +7055,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
|
|
|
7079
7055
|
</ion-item>
|
|
7080
7056
|
|
|
7081
7057
|
<!-- No results message -->
|
|
7082
|
-
<ion-item *ngIf="filteredOptions
|
|
7058
|
+
<ion-item *ngIf="filteredOptions.length === 0" class="no-results">
|
|
7083
7059
|
<ion-label color="medium">
|
|
7084
|
-
{{ searchTerm
|
|
7060
|
+
{{ searchTerm ? 'No se encontraron resultados' : 'No hay opciones disponibles' }}
|
|
7085
7061
|
<!-- Debug info -->
|
|
7086
|
-
<br><small>Debug: {{ filteredOptions
|
|
7062
|
+
<br><small>Debug: {{ filteredOptions.length }} opciones | Props: {{ !!props }} | Search: "{{ searchTerm }}"</small>
|
|
7087
7063
|
</ion-label>
|
|
7088
7064
|
</ion-item>
|
|
7089
7065
|
</ion-list>
|
|
@@ -8294,6 +8270,7 @@ class FormComponent {
|
|
|
8294
8270
|
section.fields
|
|
8295
8271
|
.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)
|
|
8296
8272
|
.forEach(field => {
|
|
8273
|
+
console.log(`[FormComponent] Tracking changes for field: ${field.name}, type: ${field.type}`);
|
|
8297
8274
|
this.trackSelectChanges(field.name);
|
|
8298
8275
|
});
|
|
8299
8276
|
});
|
|
@@ -8304,15 +8281,27 @@ class FormComponent {
|
|
|
8304
8281
|
}
|
|
8305
8282
|
trackSelectChanges(fieldName) {
|
|
8306
8283
|
const control = this.getControl(fieldName);
|
|
8284
|
+
// Initialize previous value
|
|
8285
|
+
this.previousValues.set(fieldName, control.value);
|
|
8307
8286
|
const subscription = control.valueChanges
|
|
8308
8287
|
.pipe(distinctUntilChanged$1()) // Evitar valores duplicados
|
|
8309
8288
|
.subscribe(value => {
|
|
8289
|
+
console.log(`[FormComponent] valueChanges for ${fieldName}:`, {
|
|
8290
|
+
field: fieldName,
|
|
8291
|
+
newValue: value,
|
|
8292
|
+
previousValue: this.previousValues.get(fieldName),
|
|
8293
|
+
willEmit: this.previousValues.get(fieldName) !== value
|
|
8294
|
+
});
|
|
8310
8295
|
// Evitar loops - solo emitir si el valor realmente cambió
|
|
8311
8296
|
const previousValue = this.previousValues.get(fieldName);
|
|
8312
8297
|
if (previousValue !== value) {
|
|
8313
8298
|
this.previousValues.set(fieldName, value);
|
|
8299
|
+
console.log(`[FormComponent] EMITTING onSelectChange:`, { field: fieldName, value });
|
|
8314
8300
|
this.onSelectChange.emit({ field: fieldName, value });
|
|
8315
8301
|
}
|
|
8302
|
+
else {
|
|
8303
|
+
console.log(`[FormComponent] SKIPPING emit - same value for ${fieldName}`);
|
|
8304
|
+
}
|
|
8316
8305
|
});
|
|
8317
8306
|
this.subscriptions.push(subscription);
|
|
8318
8307
|
}
|
|
@@ -8379,6 +8368,19 @@ class FormComponent {
|
|
|
8379
8368
|
data: this.props,
|
|
8380
8369
|
};
|
|
8381
8370
|
}
|
|
8371
|
+
// Helper method to reset field value without triggering change event
|
|
8372
|
+
resetField(fieldName, emitEvent = false) {
|
|
8373
|
+
const control = this.getControl(fieldName);
|
|
8374
|
+
const currentValue = control.value;
|
|
8375
|
+
console.log(`[FormComponent] Resetting field ${fieldName}:`, { currentValue, emitEvent });
|
|
8376
|
+
// Update our cache to avoid triggering change event
|
|
8377
|
+
if (!emitEvent) {
|
|
8378
|
+
this.previousValues.set(fieldName, '');
|
|
8379
|
+
}
|
|
8380
|
+
control.setValue('', { emitEvent });
|
|
8381
|
+
control.markAsPristine();
|
|
8382
|
+
control.markAsUntouched();
|
|
8383
|
+
}
|
|
8382
8384
|
get actions() {
|
|
8383
8385
|
if (!this.form) {
|
|
8384
8386
|
return [];
|