angular-matecu 4.1.0 → 4.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. package/fesm2022/angular-matecu.mjs +1740 -0
  2. package/fesm2022/angular-matecu.mjs.map +1 -0
  3. package/package.json +16 -3
  4. package/types/angular-matecu.d.ts +490 -0
  5. package/ng-package.json +0 -7
  6. package/src/css/main.css +0 -1
  7. package/src/lib/components/matecu-alert-box/matecu-alert-box.component.html +0 -11
  8. package/src/lib/components/matecu-alert-box/matecu-alert-box.component.scss +0 -39
  9. package/src/lib/components/matecu-alert-box/matecu-alert-box.component.spec.ts +0 -25
  10. package/src/lib/components/matecu-alert-box/matecu-alert-box.component.ts +0 -60
  11. package/src/lib/components/matecu-alert-dialog/matecu-alert-dialog.component.html +0 -24
  12. package/src/lib/components/matecu-alert-dialog/matecu-alert-dialog.component.scss +0 -5
  13. package/src/lib/components/matecu-alert-dialog/matecu-alert-dialog.component.spec.ts +0 -25
  14. package/src/lib/components/matecu-alert-dialog/matecu-alert-dialog.component.ts +0 -53
  15. package/src/lib/components/matecu-alert-snack-bar/matecu-alert-snack-bar.component.html +0 -10
  16. package/src/lib/components/matecu-alert-snack-bar/matecu-alert-snack-bar.component.scss +0 -34
  17. package/src/lib/components/matecu-alert-snack-bar/matecu-alert-snack-bar.component.spec.ts +0 -25
  18. package/src/lib/components/matecu-alert-snack-bar/matecu-alert-snack-bar.component.ts +0 -45
  19. package/src/lib/components/matecu-autocomplete/matecu-autocomplete.html +0 -36
  20. package/src/lib/components/matecu-autocomplete/matecu-autocomplete.scss +0 -56
  21. package/src/lib/components/matecu-autocomplete/matecu-autocomplete.spec.ts +0 -23
  22. package/src/lib/components/matecu-autocomplete/matecu-autocomplete.ts +0 -336
  23. package/src/lib/components/matecu-autocomplete-multiple/matecu-autocomplete-multiple.html +0 -64
  24. package/src/lib/components/matecu-autocomplete-multiple/matecu-autocomplete-multiple.scss +0 -23
  25. package/src/lib/components/matecu-autocomplete-multiple/matecu-autocomplete-multiple.spec.ts +0 -23
  26. package/src/lib/components/matecu-autocomplete-multiple/matecu-autocomplete-multiple.ts +0 -314
  27. package/src/lib/components/matecu-file-input/matecu-file-input-customization.md +0 -284
  28. package/src/lib/components/matecu-file-input/matecu-file-input.example.md +0 -228
  29. package/src/lib/components/matecu-file-input/matecu-file-input.html +0 -128
  30. package/src/lib/components/matecu-file-input/matecu-file-input.scss +0 -461
  31. package/src/lib/components/matecu-file-input/matecu-file-input.spec.ts +0 -340
  32. package/src/lib/components/matecu-file-input/matecu-file-input.ts +0 -438
  33. package/src/lib/components/matecu-spinner/matecu-spinner.component.css +0 -15
  34. package/src/lib/components/matecu-spinner/matecu-spinner.component.html +0 -44
  35. package/src/lib/components/matecu-spinner/matecu-spinner.component.spec.ts +0 -25
  36. package/src/lib/components/matecu-spinner/matecu-spinner.component.ts +0 -54
  37. package/src/lib/components/matecu-spinner/spinner-loader.component.scss +0 -13
  38. package/src/lib/components/matecu-topbar-action/matecu-topbar-action.component.html +0 -1
  39. package/src/lib/components/matecu-topbar-action/matecu-topbar-action.component.scss +0 -19
  40. package/src/lib/components/matecu-topbar-action/matecu-topbar-action.component.spec.ts +0 -25
  41. package/src/lib/components/matecu-topbar-action/matecu-topbar-action.component.ts +0 -14
  42. package/src/lib/components/matecu-topbar-body/matecu-topbar-body.component.html +0 -1
  43. package/src/lib/components/matecu-topbar-body/matecu-topbar-body.component.scss +0 -14
  44. package/src/lib/components/matecu-topbar-body/matecu-topbar-body.component.spec.ts +0 -25
  45. package/src/lib/components/matecu-topbar-body/matecu-topbar-body.component.ts +0 -11
  46. package/src/lib/components/matecu-topbar-fab/matecu-topbar-fab.component.html +0 -3
  47. package/src/lib/components/matecu-topbar-fab/matecu-topbar-fab.component.scss +0 -19
  48. package/src/lib/components/matecu-topbar-fab/matecu-topbar-fab.component.spec.ts +0 -25
  49. package/src/lib/components/matecu-topbar-fab/matecu-topbar-fab.component.ts +0 -31
  50. package/src/lib/components/matecu-topbar-header-column/matecu-topbar-header-column.component.html +0 -1
  51. package/src/lib/components/matecu-topbar-header-column/matecu-topbar-header-column.component.scss +0 -8
  52. package/src/lib/components/matecu-topbar-header-column/matecu-topbar-header-column.component.spec.ts +0 -23
  53. package/src/lib/components/matecu-topbar-header-column/matecu-topbar-header-column.component.ts +0 -11
  54. package/src/lib/components/matecu-topbar-header-row/matecu-topbar-header-row.component.html +0 -9
  55. package/src/lib/components/matecu-topbar-header-row/matecu-topbar-header-row.component.scss +0 -34
  56. package/src/lib/components/matecu-topbar-header-row/matecu-topbar-header-row.component.spec.ts +0 -23
  57. package/src/lib/components/matecu-topbar-header-row/matecu-topbar-header-row.component.ts +0 -18
  58. package/src/lib/components/matecu-topbar-layout/matecu-topbar-layout.component.html +0 -7
  59. package/src/lib/components/matecu-topbar-layout/matecu-topbar-layout.component.scss +0 -49
  60. package/src/lib/components/matecu-topbar-layout/matecu-topbar-layout.component.spec.ts +0 -25
  61. package/src/lib/components/matecu-topbar-layout/matecu-topbar-layout.component.ts +0 -112
  62. package/src/lib/components/matecu-topbar-search/matecu-topbar-search.component.html +0 -20
  63. package/src/lib/components/matecu-topbar-search/matecu-topbar-search.component.scss +0 -90
  64. package/src/lib/components/matecu-topbar-search/matecu-topbar-search.component.spec.ts +0 -25
  65. package/src/lib/components/matecu-topbar-search/matecu-topbar-search.component.ts +0 -92
  66. package/src/lib/components/matecu-topbar-title/matecu-topbar-title.component.html +0 -1
  67. package/src/lib/components/matecu-topbar-title/matecu-topbar-title.component.scss +0 -91
  68. package/src/lib/components/matecu-topbar-title/matecu-topbar-title.component.spec.ts +0 -25
  69. package/src/lib/components/matecu-topbar-title/matecu-topbar-title.component.ts +0 -14
  70. package/src/lib/modules/matecu-alert-box/matecu-alert-box.module.ts +0 -16
  71. package/src/lib/modules/matecu-spinner/matecu-spinner.module.ts +0 -14
  72. package/src/lib/modules/matecu-topbar-layout/matecu-topbar-layout.module.ts +0 -45
  73. package/src/lib/services/matecu-snack-bar.service.spec.ts +0 -16
  74. package/src/lib/services/matecu-snack-bar.service.ts +0 -66
  75. package/src/lib/services/matecu-spinner.service.spec.ts +0 -16
  76. package/src/lib/services/matecu-spinner.service.ts +0 -39
  77. package/src/lib/types/matecu-alert-dialog.ts +0 -10
  78. package/src/lib/types/matecu-alert-snackbar.ts +0 -8
  79. package/src/lib/types/matecu-altert-box-type.ts +0 -6
  80. package/src/lib/types/matecu-autocomplete.ts +0 -5
  81. package/src/public-api.ts +0 -42
  82. package/src/scss/main.scss +0 -2
  83. package/src/scss/matecu-spinner-module/_matecu-spinner-component.scss +0 -8
  84. package/src/scss/matecu-topbar-layout/_variables.scss +0 -2
  85. package/tsconfig.lib.json +0 -17
  86. package/tsconfig.lib.prod.json +0 -11
  87. package/tsconfig.spec.json +0 -15
@@ -1,25 +0,0 @@
1
- import { ComponentFixture, TestBed } from '@angular/core/testing';
2
-
3
- import { MatecuAlertDialogComponent } from './matecu-alert-dialog.component';
4
-
5
- describe('MatecuAlertDialogComponent', () => {
6
- let component: MatecuAlertDialogComponent;
7
- let fixture: ComponentFixture<MatecuAlertDialogComponent>;
8
-
9
- beforeEach(async () => {
10
- await TestBed.configureTestingModule({
11
- declarations: [ MatecuAlertDialogComponent ]
12
- })
13
- .compileComponents();
14
- });
15
-
16
- beforeEach(() => {
17
- fixture = TestBed.createComponent(MatecuAlertDialogComponent);
18
- component = fixture.componentInstance;
19
- fixture.detectChanges();
20
- });
21
-
22
- it('should create', () => {
23
- expect(component).toBeTruthy();
24
- });
25
- });
@@ -1,53 +0,0 @@
1
- import { CommonModule } from '@angular/common';
2
- import { Component, Inject, OnInit } from '@angular/core';
3
- import { MatButtonModule } from '@angular/material/button';
4
- import { MAT_DIALOG_DATA, MatDialogRef, MatDialogModule } from '@angular/material/dialog';
5
- import { MatIconModule } from '@angular/material/icon';
6
- import { MatecuAlertDialogData } from '../../types/matecu-alert-dialog';
7
- import { MatecuAlertBoxComponent } from '../matecu-alert-box/matecu-alert-box.component';
8
- @Component({
9
- selector: 'matecu-alert-dialog',
10
- templateUrl: './matecu-alert-dialog.component.html',
11
- styleUrls: ['./matecu-alert-dialog.component.scss'],
12
- standalone: true,
13
- imports: [CommonModule, MatDialogModule, MatIconModule, MatecuAlertBoxComponent, MatButtonModule]
14
- })
15
- export class MatecuAlertDialogComponent implements OnInit {
16
- hasTitle = false;
17
- hasDismissBtn = false;
18
- hasActionBtn = false;
19
- showActions = false;
20
- constructor(
21
- @Inject(MAT_DIALOG_DATA) public dialogData: MatecuAlertDialogData,
22
- public dialogRef: MatDialogRef<MatecuAlertDialogComponent>
23
- ) {
24
- this.setHasTitle();
25
- this.setHasDismissBtn();
26
- this.setHasActionBtn();
27
- this.setShowActions();
28
- }
29
-
30
- ngOnInit(): void { }
31
-
32
- activateAction(): void {
33
- this.dialogRef.close(true);
34
- }
35
-
36
- private setHasTitle(): void {
37
- this.hasTitle = this.isValidString(this.dialogData.title);
38
- }
39
- private setHasDismissBtn(): void {
40
- this.hasDismissBtn = this.isValidString(this.dialogData.dismissText);
41
- }
42
-
43
- private setHasActionBtn(): void {
44
- this.hasActionBtn = this.isValidString(this.dialogData.action);
45
- }
46
- private setShowActions(): void {
47
- this.showActions = this.hasActionBtn || this.hasDismissBtn;
48
- }
49
- private isValidString(str: string | null | undefined): boolean {
50
- const isValid = typeof str === 'string' && str.trim().length > 0;
51
- return isValid;
52
- }
53
- }
@@ -1,10 +0,0 @@
1
- <div>
2
- <strong>{{title}}</strong> &nbsp;
3
- <span>{{message}}</span>
4
- </div>
5
- @if(action?.length){
6
-
7
- <div>
8
- <button mat-button color="accent" (click)="dismiss()">{{action}}</button>
9
- </div>
10
- }
@@ -1,34 +0,0 @@
1
- :host.matecu-alert-snackbar {
2
- display: flex;
3
- align-items: center;
4
- > * {
5
- &:not(:first-child) {
6
- margin-left: 10px;
7
-
8
- }
9
-
10
- }
11
- &--success {
12
- strong {
13
-
14
- color: rgb(32, 219, 32);
15
- }
16
- }
17
- &--danger {
18
- strong {
19
- color: red;
20
- }
21
- }
22
- &--warning {
23
- strong {
24
- color: orange;
25
- }
26
- }
27
- &--info {
28
- strong {
29
- color: rgb(7, 160, 237)
30
- }
31
- }
32
-
33
-
34
- }
@@ -1,25 +0,0 @@
1
- import { ComponentFixture, TestBed } from '@angular/core/testing';
2
-
3
- import { MatecuAlertSnackBarComponent } from './matecu-alert-snack-bar.component';
4
-
5
- describe('MatecuAlertSnackBarComponent', () => {
6
- let component: MatecuAlertSnackBarComponent;
7
- let fixture: ComponentFixture<MatecuAlertSnackBarComponent>;
8
-
9
- beforeEach(async () => {
10
- await TestBed.configureTestingModule({
11
- declarations: [ MatecuAlertSnackBarComponent ]
12
- })
13
- .compileComponents();
14
- });
15
-
16
- beforeEach(() => {
17
- fixture = TestBed.createComponent(MatecuAlertSnackBarComponent);
18
- component = fixture.componentInstance;
19
- fixture.detectChanges();
20
- });
21
-
22
- it('should create', () => {
23
- expect(component).toBeTruthy();
24
- });
25
- });
@@ -1,45 +0,0 @@
1
- import { CommonModule } from '@angular/common';
2
- import { Component, HostBinding, Inject, OnInit } from '@angular/core';
3
- import { MatButtonModule } from '@angular/material/button';
4
- import { MatSnackBarModule, MatSnackBarRef, MAT_SNACK_BAR_DATA } from '@angular/material/snack-bar';
5
- import { MatecuAlertSnackBarData } from '../../types/matecu-alert-snackbar';
6
- import { MatecuAlertBoxType } from '../../types/matecu-altert-box-type';
7
-
8
- @Component({
9
- selector: 'matecu-alert-snack-bar',
10
- templateUrl: './matecu-alert-snack-bar.component.html',
11
- styleUrls: ['./matecu-alert-snack-bar.component.scss'],
12
- standalone: true,
13
- imports: [CommonModule, MatButtonModule, MatSnackBarModule]
14
- })
15
- export class MatecuAlertSnackBarComponent {
16
-
17
- title: string;
18
- message: string;
19
- action?: string;
20
- classNameBase = 'matecu-alert-snackbar';
21
- alertColor!: MatecuAlertBoxType;
22
- get color(): MatecuAlertBoxType {
23
- return this.alertColor;
24
- }
25
- set color(value: MatecuAlertBoxType) {
26
- this.alertColor = value;
27
- this.className = `${this.classNameBase} ${this.classNameBase}--${value}`;
28
- }
29
- @HostBinding('class') className = this.classNameBase;
30
- constructor(
31
- @Inject(MAT_SNACK_BAR_DATA) public data: MatecuAlertSnackBarData,
32
- private snackBarRef: MatSnackBarRef<MatecuAlertSnackBarComponent>) {
33
- this.title = data.title;
34
- this.message = data.message;
35
- this.action = data.action;
36
- this.color = data.type;
37
-
38
- }
39
-
40
- dismiss(): void {
41
- this.snackBarRef.dismissWithAction();
42
- }
43
-
44
-
45
- }
@@ -1,36 +0,0 @@
1
- <input matInput type="text" matInput [formControl]="inputControl" [matAutocomplete]="auto" [class.loading]="loading"
2
- [disabled]="disabled" [readonly]="readonly" />
3
-
4
-
5
- <mat-autocomplete #auto="matAutocomplete" (optionSelected)="onOptionSelected($event.option.value)"
6
- [displayWith]="displayLabel">
7
-
8
- @for (option of filteredOptions$ | async; track $index) {
9
-
10
- <mat-option [value]="option[0]">
11
- {{ option[1] }}
12
- </mat-option>
13
-
14
- }
15
-
16
- <!-- Crear nuevo -->
17
- @if(allowCreate){
18
-
19
- <mat-divider></mat-divider>
20
-
21
- @if(showCreateOption ){
22
- <mat-option (click)="onCreateClick()" class="create-option">
23
- <span>➕</span><span>" {{ inputControl.value }}"</span>
24
-
25
- </mat-option>
26
- }
27
- }
28
-
29
- </mat-autocomplete>
30
-
31
- <!-- Spinner -->
32
- @if(loading){
33
- <mat-progress-spinner class="loading-spinner" diameter="20" mode="indeterminate"
34
- style="position:absolute; right:10px; top:50%; transform:translateY(-50%);">
35
- </mat-progress-spinner>
36
- }
@@ -1,56 +0,0 @@
1
- :host {
2
- display: block;
3
- position: relative;
4
- box-sizing: border-box;
5
- width: 100%;
6
- input {
7
- border: none;
8
- background-color: transparent;
9
- outline: none;
10
- }
11
- .create-option {
12
- display: flex;
13
- justify-content: space-between;
14
- align-items: center;
15
- &:first-child {
16
- margin-right: auto;
17
- color: green;
18
- }
19
- }
20
- }
21
-
22
- // /* Spinner alineado como suffix de Material */
23
- .loading-spinner {
24
- position: absolute;
25
- right: 12px;
26
- top: 50%;
27
- transform: translateY(-50%);
28
- pointer-events: none;
29
- }
30
-
31
- // /* Ajuste para que no choque con el spinner */
32
- input.loading {
33
- padding-right: 40px;
34
- }
35
-
36
- // /* Opción de crear */
37
- .create-option {
38
- font-style: italic;
39
- opacity: 0.85;
40
- }
41
-
42
- /* Hover más suave */
43
- ::ng-deep .mat-mdc-option.create-option:hover {
44
- background: rgba(0, 0, 0, 0.04);
45
- }
46
-
47
- /* Divider más discreto */
48
- ::ng-deep .mat-mdc-autocomplete-panel mat-divider {
49
- margin: 4px 0;
50
- }
51
-
52
- /* Estado disabled */
53
- :host(.mat-mdc-input-disabled) {
54
- opacity: 0.6;
55
- pointer-events: none;
56
- }
@@ -1,23 +0,0 @@
1
- import { ComponentFixture, TestBed } from '@angular/core/testing';
2
-
3
- import { MatecuAutocomplete } from './matecu-autocomplete';
4
-
5
- describe('MatecuAutocomplete', () => {
6
- let component: MatecuAutocomplete;
7
- let fixture: ComponentFixture<MatecuAutocomplete>;
8
-
9
- beforeEach(async () => {
10
- await TestBed.configureTestingModule({
11
- imports: [MatecuAutocomplete]
12
- })
13
- .compileComponents();
14
-
15
- fixture = TestBed.createComponent(MatecuAutocomplete);
16
- component = fixture.componentInstance;
17
- await fixture.whenStable();
18
- });
19
-
20
- it('should create', () => {
21
- expect(component).toBeTruthy();
22
- });
23
- });
@@ -1,336 +0,0 @@
1
- import { CommonModule } from '@angular/common';
2
- import {
3
- Component,
4
- Input,
5
- Output,
6
- EventEmitter,
7
- OnChanges,
8
- SimpleChanges,
9
- OnInit,
10
- OnDestroy,
11
- ElementRef,
12
- DoCheck,
13
- Injector,
14
- } from '@angular/core';
15
- import {
16
- ControlValueAccessor,
17
- FormsModule,
18
- NG_VALUE_ACCESSOR,
19
- ReactiveFormsModule,
20
- NgControl,
21
- } from '@angular/forms';
22
- import { FormControl } from '@angular/forms';
23
- import { MatAutocompleteModule } from '@angular/material/autocomplete';
24
- import { MatDividerModule } from '@angular/material/divider';
25
- import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
26
- import { MatFormFieldControl } from '@angular/material/form-field';
27
- import { Observable, startWith, map, Subject, tap, combineLatest, debounceTime } from 'rxjs';
28
- import { coerceBooleanProperty } from '@angular/cdk/coercion';
29
- import { FocusMonitor } from '@angular/cdk/a11y';
30
- import {
31
- MatecuAutocompleteFilterFn,
32
- MatecuAutocompleteOption,
33
- } from '../../types/matecu-autocomplete';
34
-
35
- @Component({
36
- selector: 'matecu-autocomplete',
37
- standalone: true,
38
- imports: [
39
- CommonModule,
40
- MatAutocompleteModule,
41
- MatDividerModule,
42
- FormsModule,
43
- ReactiveFormsModule,
44
- MatProgressSpinnerModule,
45
- ],
46
- templateUrl: './matecu-autocomplete.html',
47
- styleUrls: ['./matecu-autocomplete.scss'],
48
- providers: [
49
- {
50
- provide: NG_VALUE_ACCESSOR,
51
- useExisting: MatecuAutocomplete,
52
- multi: true,
53
- },
54
- {
55
- provide: MatFormFieldControl,
56
- useExisting: MatecuAutocomplete,
57
- },
58
- ],
59
- })
60
- export class MatecuAutocomplete
61
- implements
62
- ControlValueAccessor,
63
- MatFormFieldControl<string>,
64
- OnChanges,
65
- OnInit,
66
- OnDestroy,
67
- DoCheck
68
- {
69
- @Input() options: MatecuAutocompleteOption[] = [];
70
- @Input() allowCreate = false;
71
- @Input() loading = false;
72
- @Input() readonly = false;
73
- @Input() filterFn: MatecuAutocompleteFilterFn = this.createFilterFn();
74
- @Input() searchChangeDebounceTime = 300;
75
- // MatFormFieldControl inputs
76
- @Input()
77
- get placeholder(): string {
78
- return this._placeholder;
79
- }
80
- set placeholder(value: string) {
81
- this._placeholder = value;
82
- this.stateChanges.next();
83
- }
84
- private _placeholder = '';
85
-
86
- @Input()
87
- get required(): boolean {
88
- return this._required;
89
- }
90
- set required(value: boolean) {
91
- this._required = coerceBooleanProperty(value);
92
- this.stateChanges.next();
93
- }
94
- private _required = false;
95
-
96
- @Input()
97
- get disabled(): boolean {
98
- return this._disabled;
99
- }
100
- set disabled(value: boolean) {
101
- this._disabled = coerceBooleanProperty(value);
102
- this.setDisabledState(this._disabled);
103
- this.stateChanges.next();
104
- }
105
- private _disabled = false;
106
-
107
- @Input()
108
- get value(): string | null {
109
- return this.internalValue;
110
- }
111
- set value(value: string | null) {
112
- this.writeValue(value);
113
- this.stateChanges.next();
114
- }
115
-
116
- @Output() searchChange = new EventEmitter<string>();
117
- @Output() create = new EventEmitter<string>();
118
- @Output() valueChange = new EventEmitter<string | null>();
119
-
120
- // MatFormFieldControl properties
121
- static nextId = 0;
122
- readonly stateChanges = new Subject<void>();
123
- readonly id = `matecu-autocomplete-${MatecuAutocomplete.nextId++}`;
124
- ngControl: NgControl | null = null;
125
- focused = false;
126
- lastSearchText: string | null = null;
127
- readonly controlType = 'matecu-autocomplete';
128
- readonly autofilled = false;
129
-
130
- inputControl = new FormControl<string | null>(null);
131
- filteredOptions$!: Observable<MatecuAutocompleteOption[]>;
132
-
133
- private internalValue: string | null = null;
134
- private focusMonitor: FocusMonitor;
135
- private elementRef: ElementRef<HTMLElement>;
136
- private injector: Injector;
137
- private optionMap = new Map<string, string>();
138
-
139
- private onChange: any = () => {};
140
- private onTouched: any = () => {};
141
- get empty(): boolean {
142
- const isEmpty = this.inputControl.value === '' || !this.inputControl.value;
143
- return isEmpty;
144
- }
145
-
146
- get shouldLabelFloat(): boolean {
147
- const shouldFloat = this.focused || !this.empty;
148
- return shouldFloat;
149
- }
150
-
151
- get errorState(): boolean {
152
- return !!(this.ngControl && this.ngControl.invalid && this.ngControl.touched);
153
- }
154
- get showCreateOption(): boolean {
155
- const value = this.inputControl.value;
156
-
157
- return (
158
- this.allowCreate &&
159
- typeof value === 'string' &&
160
- this.internalValue !== value &&
161
- this.options.some((option) => option[1].toLowerCase() === value.toLowerCase()) === false
162
- );
163
- }
164
-
165
- constructor(focusMonitor: FocusMonitor, elementRef: ElementRef<HTMLElement>, injector: Injector) {
166
- this.focusMonitor = focusMonitor;
167
- this.elementRef = elementRef;
168
- this.injector = injector;
169
- }
170
-
171
- ngOnInit() {
172
- // Intentar obtener NgControl de forma segura
173
- try {
174
- this.ngControl = this.injector.get(NgControl, null);
175
- } catch (error) {
176
- // Ignorar si no se puede obtener NgControl
177
- this.ngControl = null;
178
- }
179
-
180
- const value$ = this.inputControl.valueChanges.pipe(startWith(this.inputControl.value ?? ''));
181
-
182
- this.filteredOptions$ = combineLatest([value$]).pipe(
183
- map(([value]) => this.filter(value ?? '')),
184
- );
185
-
186
- this.inputControl.valueChanges
187
- .pipe(
188
- // Almacena el último valor valido para la búsqueda esto se utiliza para enviar una emición para crear un elemento
189
- tap((value) => (this.lastSearchText = value ?? this.lastSearchText)),
190
- // CUando se escribe algo se limpian los valores seleccionados para que el usuario pueda seleccionar una opción o crear una nueva
191
- tap(() => this.clearValue()),
192
- debounceTime(this.searchChangeDebounceTime),
193
- )
194
- .subscribe((value) => {
195
- queueMicrotask(() => {
196
- this.searchChange.emit(value ?? '');
197
- });
198
- });
199
-
200
- this.focusMonitor.monitor(this.elementRef, true).subscribe((focused) => {
201
- if (!!focused !== this.focused) {
202
- this.focused = !!focused;
203
- this.stateChanges.next();
204
- }
205
- });
206
- }
207
-
208
- ngOnDestroy() {
209
- this.stateChanges.complete();
210
- this.focusMonitor.stopMonitoring(this.elementRef);
211
- }
212
-
213
- ngDoCheck() {
214
- if (this.ngControl) {
215
- const newErrorState = !!(this.ngControl.invalid && this.ngControl.touched);
216
- if (newErrorState !== this.errorState) {
217
- this.stateChanges.next();
218
- }
219
- }
220
- }
221
-
222
- ngOnChanges(changes: SimpleChanges) {
223
- if (changes['options']) {
224
- this.rebuildOptionMap();
225
- this.updateInputLabelFromValue();
226
- this.stateChanges.next();
227
- }
228
-
229
- if (changes['placeholder'] || changes['required'] || changes['disabled']) {
230
- this.stateChanges.next();
231
- }
232
- }
233
-
234
- private filter(search: string): MatecuAutocompleteOption[] {
235
- return this.options.filter((option) => this.filterFn(option[1], search));
236
- }
237
-
238
- private createFilterFn(): (v1: string, v2: string) => boolean {
239
- return (v1: string, v2: string): boolean => {
240
- return v1.toLowerCase().includes(v2.toLowerCase());
241
- };
242
- }
243
-
244
- displayLabel = (value: string | null): string => {
245
- if (value === null || value === undefined) return '';
246
-
247
- if (!Array.isArray(this.options)) {
248
- return '';
249
- }
250
- return this.optionMap.get(value) ?? '';
251
- };
252
-
253
- onOptionSelected(value: string) {
254
- if (!value || this.readonly || this.disabled) {
255
- return;
256
- }
257
- this.internalValue = value;
258
- this.inputControl.setValue(value, { emitEvent: false });
259
- this.onChange(value);
260
- this.onTouched();
261
- this.valueChange.emit(value);
262
- }
263
-
264
- onCreateClick() {
265
- if (
266
- !this.lastSearchText ||
267
- this.lastSearchText.trim() === '' ||
268
- this.options.some(
269
- (option) => option[1].toLowerCase() === this.lastSearchText!.toLowerCase(),
270
- ) ||
271
- this.options.some((option) => option[1].toLowerCase() === this.lastSearchText!.toLowerCase())
272
- ) {
273
- return;
274
- }
275
- this.create.emit(this.lastSearchText);
276
- }
277
-
278
- private updateInputLabelFromValue() {
279
- if (this.internalValue !== null) {
280
- const label = this.displayLabel(this.internalValue);
281
- this.inputControl.setValue(label, { emitEvent: false });
282
- }
283
- }
284
-
285
- // ControlValueAccessor
286
-
287
- writeValue(value: string | null): void {
288
- this.internalValue = value;
289
- this.updateInputLabelFromValue();
290
- this.stateChanges.next();
291
- }
292
-
293
- registerOnChange(fn: any): void {
294
- this.onChange = fn;
295
- }
296
-
297
- registerOnTouched(fn: any): void {
298
- this.onTouched = fn;
299
- }
300
-
301
- setDisabledState(isDisabled: boolean): void {
302
- this._disabled = isDisabled;
303
- isDisabled ? this.inputControl.disable() : this.inputControl.enable();
304
- this.stateChanges.next();
305
- }
306
-
307
- // MatFormFieldControl methods
308
- focus(options?: FocusOptions): void {
309
- this.elementRef.nativeElement.querySelector('input')?.focus(options);
310
- }
311
-
312
- onContainerClick(): void {
313
- this.focus();
314
- }
315
-
316
- setDescribedByIds(ids: string[]): void {
317
- const input = this.elementRef.nativeElement.querySelector('input');
318
- if (input) {
319
- input.setAttribute('aria-describedby', ids.join(' '));
320
- }
321
- }
322
- private rebuildOptionMap() {
323
- this.optionMap.clear();
324
-
325
- for (const [value, label] of this.options ?? []) {
326
- this.optionMap.set(value, label);
327
- }
328
- }
329
-
330
- private clearValue() {
331
- this.internalValue = null;
332
- this.onChange(null);
333
- this.onTouched();
334
- this.valueChange.emit(null);
335
- }
336
- }