Skip to content

Commit 41fa303

Browse files
committed
Add DropdownCoordinatorService
1 parent 73796d7 commit 41fa303

6 files changed

Lines changed: 60 additions & 14 deletions

File tree

CLAUDE.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ Este repo es además una evolución de lo que antes era `@some-angular-utils/fil
1515

1616
La librería pasó por un rename que dejó residuos. El componente actual (`SAUDateRangePickerModule`, selector `sau-date-range-picker`, archivo `date-range-picker.component.ts`) antes se llamaba `SAUDateRangePickerModule` con selector `<sau-date-range-picker>`, y vivía en `projects/some-angular-utils/date-picker/.../components/sau-date-range-picker/`. El nombre de carpeta del proyecto Angular pasó de `date-picker` a `date-range-picker` (ahora sí coincide con el nombre del paquete npm), y la clase pasó de `sau-date-range-picker` a `date-range-picker`. Pero el rename quedó **incompleto**:
1717

18-
- `angular.json` (target de librería, líneas ~67-90) y `tsconfig.json` (`references`) todavía apuntan a `projects/some-angular-utils/date-picker/...`, una ruta que **ya no existe en disco** (ahora es `projects/some-angular-utils/date-range-picker/...`). El build real (`npm run build:lib`) no pasa por ahí — invoca `ng-packagr` directamente sobre `projects/some-angular-utils/date-range-picker/ng-package.json` — así que el build funciona a pesar de la referencia rota, pero cualquier tooling que sí use esas referencias (IDE, `ng test` del proyecto librería, etc.) puede fallar o resolver mal.
18+
- ~~`angular.json` (target de librería) y `tsconfig.json` (`references`) apuntaban a `projects/some-angular-utils/date-picker/...`~~**corregido** (2026-08-23): ambos ya apuntan a `projects/some-angular-utils/date-range-picker/...`. Antes de la corrección, el build real (`npm run build:lib`) funcionaba igual porque invoca `ng-packagr` directamente sobre `ng-package.json` sin pasar por esas referencias, pero cualquier tooling que sí las usara (IDE/tsserver, `ng test` del proyecto librería) resolvía mal — en concreto, el `tsconfig.json` raíz no lograba asociar los archivos de la librería a un proyecto con `experimentalDecorators: true`, lo que producía errores falsos tipo `Decorators are not valid here.ts(1206)` en decoradores de parámetro (p. ej. `@Optional()` en el constructor de `date-range-picker.component.ts`) aunque el código fuera válido. Si reaparece ese error, reinicia el TS server del IDE.
1919
- `src/app/components/installation/installation.ts` (snippet de uso) y `src/app/components/demos/demos.html` (texto de la sección "See it in action") todavía muestran/mencionan `<sau-date-range-picker>` y `SAUDateRangePickerModule` en vez del nombre real `<sau-date-range-picker>` / `SAUDateRangePickerModule`. Es texto de la showcase desactualizado, no refleja cómo se usa la librería realmente — si tocas esos archivos, vale la pena corregirlo de paso.
2020

2121
Si vuelves a encontrar `sau-date-range-picker`/`SAUDateRangePickerModule` en algún sitio no listado arriba, asume que es otro resquicio del mismo rename incompleto, no una API alternativa vigente.
@@ -26,11 +26,10 @@ Si vuelves a encontrar `sau-date-range-picker`/`SAUDateRangePickerModule` en alg
2626
date-input/
2727
├── CLAUDE.md
2828
├── README.md
29-
├── angular.json # ⚠️ el target de librería referencia la ruta vieja "date-picker" (ver arriba)
29+
├── angular.json
3030
├── package.json
3131
├── .postcssrc.json # Tailwind v4 vía @tailwindcss/postcss
3232
├── tsconfig.json # mapea "@some-angular-utils/date-range-picker" -> dist/some-angular-utils/date-range-picker
33-
│ # (su "references" también apunta a la ruta vieja "date-picker", ver arriba)
3433
3534
├── src/ # app showcase (proyecto "date-range-picker")
3635
│ ├── index.html

angular.json

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,26 +66,26 @@
6666
},
6767
"@some-angular-utils/date-range-picker": {
6868
"projectType": "library",
69-
"root": "projects/some-angular-utils/date-picker",
70-
"sourceRoot": "projects/some-angular-utils/date-picker/src",
69+
"root": "projects/some-angular-utils/date-range-picker",
70+
"sourceRoot": "projects/some-angular-utils/date-range-picker/src",
7171
"prefix": "lib",
7272
"architect": {
7373
"build": {
7474
"builder": "@angular/build:ng-packagr",
7575
"configurations": {
7676
"production": {
77-
"tsConfig": "projects/some-angular-utils/date-picker/tsconfig.lib.prod.json"
77+
"tsConfig": "projects/some-angular-utils/date-range-picker/tsconfig.lib.prod.json"
7878
},
7979
"development": {
80-
"tsConfig": "projects/some-angular-utils/date-picker/tsconfig.lib.json"
80+
"tsConfig": "projects/some-angular-utils/date-range-picker/tsconfig.lib.json"
8181
}
8282
},
8383
"defaultConfiguration": "production"
8484
},
8585
"test": {
8686
"builder": "@angular/build:unit-test",
8787
"options": {
88-
"tsConfig": "projects/some-angular-utils/date-picker/tsconfig.spec.json"
88+
"tsConfig": "projects/some-angular-utils/date-range-picker/tsconfig.spec.json"
8989
}
9090
}
9191
}

projects/some-angular-utils/date-range-picker/src/lib/date-range-picker.component.ts

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
1-
import { Component, Input, Output, EventEmitter, signal, computed, effect, ElementRef, HostListener } from '@angular/core';
1+
import { Component, Input, Output, EventEmitter, signal, computed, effect, ElementRef, HostListener, OnInit, OnDestroy, Optional } from '@angular/core';
22
import { CommonModule } from '@angular/common';
33
import { AbstractControl, FormControl, ReactiveFormsModule } from '@angular/forms';
4+
import { Subscription } from 'rxjs';
5+
6+
import { DropdownCoordinatorService } from './dropdown-coordinator.service';
47

58
export interface DateRangeOption {
69
label: string;
@@ -14,7 +17,7 @@ export interface DateRangeOption {
1417
styleUrl: './date-range-picker.component.scss',
1518
imports: [CommonModule, ReactiveFormsModule]
1619
})
17-
export class SAUDateRangePickerModule {
20+
export class SAUDateRangePickerModule implements OnInit, OnDestroy {
1821
@Input() label = '';
1922
@Input() placeholder = 'Selecciona rango de fechas';
2023

@@ -59,14 +62,31 @@ export class SAUDateRangePickerModule {
5962
return `${this.formatDate(start)} - ${this.formatDate(end)}`;
6063
});
6164

62-
constructor(private elementRef: ElementRef) {
65+
private openedSubscription?: Subscription;
66+
67+
constructor(private elementRef: ElementRef, @Optional() private coordinator?: DropdownCoordinatorService) {
6368
effect(() => {
6469
if (this.showCalendar()) {
6570
this.selectingEndDate.set(false);
6671
}
6772
});
6873
}
6974

75+
ngOnInit() {
76+
// Si un custom-select/custom-input del mismo host se abre, cerramos este
77+
this.openedSubscription = this.coordinator?.opened$.subscribe(source => {
78+
if (source !== this && (this.showDropdown() || this.showCalendar())) {
79+
this.showDropdown.set(false);
80+
this.showCalendar.set(false);
81+
this.hoveredDate.set(null);
82+
}
83+
});
84+
}
85+
86+
ngOnDestroy() {
87+
this.openedSubscription?.unsubscribe();
88+
}
89+
7090
@HostListener('document:click', ['$event'])
7191
clickOut(event: MouseEvent) {
7292
if (!this.elementRef.nativeElement.contains(event.target)) {
@@ -152,7 +172,14 @@ export class SAUDateRangePickerModule {
152172
this.showCalendar.set(false);
153173
this.showDropdown.set(false);
154174
} else {
155-
this.showDropdown.set(!this.showDropdown());
175+
const opening = !this.showDropdown();
176+
this.showDropdown.set(opening);
177+
// stopPropagation() de arriba impide que el clickOut() de document de otros
178+
// custom-select/custom-input/sau-date-range-picker se dispare, así que avisamos
179+
// explícitamente para que se cierren
180+
if (opening) {
181+
this.coordinator?.notifyOpened(this);
182+
}
156183
}
157184
}
158185

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import { Injectable } from '@angular/core';
2+
import { Subject } from 'rxjs';
3+
4+
/**
5+
* Coordina el cierre de los distintos desplegables (sau-date-range-picker,
6+
* custom-select, custom-input) que convivan dentro de un mismo host (p.ej. <sau-filter>).
7+
* Cada uno hace stopPropagation() al abrirse, así que el clickOut() de document
8+
* de los demás nunca se dispara; sin este servicio, dos desplegables abiertos
9+
* a la vez se quedan ambos abiertos.
10+
*/
11+
@Injectable()
12+
export class DropdownCoordinatorService {
13+
private readonly openedSource = new Subject<unknown>();
14+
readonly opened$ = this.openedSource.asObservable();
15+
16+
notifyOpened(source: unknown): void {
17+
this.openedSource.next(source);
18+
}
19+
}

projects/some-angular-utils/date-range-picker/src/public-api.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@
33
*/
44

55
export * from './lib/date-range-picker.component';
6+
export * from './lib/dropdown-coordinator.service';

tsconfig.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,10 @@
3535
"path": "./tsconfig.spec.json"
3636
},
3737
{
38-
"path": "./projects/some-angular-utils/date-picker/tsconfig.lib.json"
38+
"path": "./projects/some-angular-utils/date-range-picker/tsconfig.lib.json"
3939
},
4040
{
41-
"path": "./projects/some-angular-utils/date-picker/tsconfig.spec.json"
41+
"path": "./projects/some-angular-utils/date-range-picker/tsconfig.spec.json"
4242
}
4343
]
4444
}

0 commit comments

Comments
 (0)