You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: CLAUDE.md
+36-18Lines changed: 36 additions & 18 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,24 +1,36 @@
1
1
# CLAUDE.md
2
2
3
+
> **Regla para Claude: mantén este archivo al día.** Tras cualquier cambio relevante en el repo (rename de componentes/carpetas, cambios de selector o de API pública, nuevas demos, scripts, gotchas descubiertos, etc.), actualiza este `CLAUDE.md` en la misma tarea — no esperes a que el usuario lo pida explícitamente. Si detectas que algo aquí ya no coincide con el código (como pasó con el rename `date-picker` → `date-range-picker`), corrígelo de inmediato.
4
+
3
5
Contexto para trabajar en este repo. Es un workspace de Angular con dos proyectos:
4
6
5
7
-**`date-range-picker`** (`src/app`) — la landing page/showcase de la librería. No es un producto real, no tiene backend.
6
-
-**`@some-angular-utils/date-range-picker`** (`projects/some-angular-utils/date-picker`) — la librería Angular publicable de verdad (el componente `<date-range-input>`). El nombre de la carpeta en disco (`date-picker`) no coincide con el nombre del paquete publicado (`date-range-picker`) — es intencional, no lo renombres para "que coincida".
8
+
-**`@some-angular-utils/date-range-picker`** (`projects/some-angular-utils/date-range-picker`) — la librería Angular publicable de verdad. El componente se selecciona como `<sau-date-range-picker>` y su clase TypeScript se llama, algo confusamente, `SAUDateRangePickerModule` (es un `@Component`, no un `NgModule` — el sufijo "Module" es solo el nombre de la clase, no refleja su naturaleza).
7
9
8
10
Este repo es hermano de `c:\Users\ADMINISTRATOR\Desktop\table` (la librería `@some-angular-utils/table`) — ambas landing pages siguen exactamente el mismo patrón (mismo navbar/hero/features/demos/installation/footer, mismo mini editor de código). Si cambias algo estructural aquí, probablemente también aplique allí, y viceversa.
9
11
10
-
Este repo es además una evolución de lo que antes era `@some-angular-utils/filter`: la librería original era un formulario de filtros genérico y configurable (`sau-filter`, con `custom-input`/`custom-select`/`date-range-input` como subcomponentes). Se hizo un pivote completo para publicar únicamente el `date-range-input` como librería independiente — `filter.ts`/`filter.html`/`filter.scss` y `custom-input`/`custom-select` se eliminaron porque solo los usaba el `sau-filter` original. Si encuentras referencias sueltas a "filter" en código, lockfiles o `dist/` antiguo que no se mencionen en este documento, son resquicios de esa migración, no parte del diseño actual.
12
+
Este repo es además una evolución de lo que antes era `@some-angular-utils/filter`: la librería original era un formulario de filtros genérico y configurable (`sau-filter`, con `custom-input`/`custom-select`/`date-range-input` como subcomponentes). Se hizo un pivote completo para publicar únicamente el selector de rango de fechas como librería independiente — `filter.ts`/`filter.html`/`filter.scss` y `custom-input`/`custom-select` se eliminaron porque solo los usaba el `sau-filter` original. Si encuentras referencias sueltas a "filter" en código, lockfiles o `dist/` antiguo (p. ej. `dist/some-angular-utils/filter`, `dist/some-angular-utils/selector`) que no se mencionen en este documento, son resquicios de esa migración, no parte del diseño actual.
13
+
14
+
## ⚠️ El componente se llamaba `date-range-input` — ya no
15
+
16
+
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 `DateRangeInputComponent` con selector `<date-range-input>`, y vivía en `projects/some-angular-utils/date-picker/.../components/date-range-input/`. 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 `date-range-input` a `date-range-picker`. Pero el rename quedó **incompleto**:
17
+
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.
19
+
-`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 `<date-range-input>` y `DateRangeInputComponent` 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.
20
+
21
+
Si vuelves a encontrar `date-range-input`/`DateRangeInputComponent` en algún sitio no listado arriba, asume que es otro resquicio del mismo rename incompleto, no una API alternativa vigente.
11
22
12
23
## Árbol del código
13
24
14
25
```
15
26
date-input/
16
27
├── CLAUDE.md
17
28
├── README.md
18
-
├── angular.json
29
+
├── angular.json # ⚠️ el target de librería referencia la ruta vieja "date-picker" (ver arriba)
└── date-range-input/ # único componente publicado: selector de rango de fechas con presets (hoy, mes actual...)
54
+
├── date-range-picker.component.ts # único componente publicado, todo en un archivo plano (sin subcarpeta components/)
55
+
├── date-range-picker.component.html
56
+
└── date-range-picker.component.scss
44
57
```
45
58
46
59
## El orden de build importa
47
60
48
-
La app importa la librería como `@some-angular-utils/date-range-picker`, que `tsconfig.json` mapea a `./dist/some-angular-utils/date-range-picker` — **no** al código fuente. Si editas algo dentro de `projects/some-angular-utils/date-picker/src`, hay que reconstruir antes de que la app lo vea:
61
+
La app importa la librería como `@some-angular-utils/date-range-picker`, que `tsconfig.json` mapea a `./dist/some-angular-utils/date-range-picker` — **no** al código fuente. Si editas algo dentro de `projects/some-angular-utils/date-range-picker/src`, hay que reconstruir antes de que la app lo vea:
49
62
50
63
```bash
51
-
npm run build:lib # ng-packagr -> dist/some-angular-utils/date-range-picker
64
+
npm run build:lib # ng-packagr -p projects/some-angular-utils/date-range-picker/ng-package.json -> dist/some-angular-utils/date-range-picker
65
+
npm run dev # build:lib + ng serve, en un solo comando (útil para arrancar de cero)
52
66
```
53
67
54
-
`ng serve` (usa Vite) pre-empaqueta dependencias y **no** recoge de forma confiable un `dist/` recién construido. Después de `build:lib`, mata y reinicia `ng serve` (o borra `.angular/cache` antes) — no asumas que el hot-reload lo detectó.
68
+
`ng serve` (usa Vite) pre-empaqueta dependencias y **no** recoge de forma confiable un `dist/` recién construido. Si `ng serve` ya está corriendo y vuelves a correr `build:lib` por separado, mata y reinicia `ng serve` (o borra `.angular/cache` antes) — no asumas que el hot-reload lo detectó.
55
69
56
70
## Storybook fue eliminado
57
71
58
-
Storybook (`.storybook/` en la raíz y en la librería, `src/stories/`, los targets `storybook`/`build-storybook` en `angular.json`, las dependencias `@storybook/*`, el workflow `publishStorybook.yml` y `debug-storybook.log`) se eliminó a propósito en favor de la app showcase de `src/app`. No lo reintroduzcas a menos que se pida explícitamente.
72
+
Storybook (`.storybook/` en la raíz y en la librería, `src/stories/`, los targets `storybook`/`build-storybook` en `angular.json`, las dependencias `@storybook/*`, el workflow `publishStorybook.yml` y `debug-storybook.log`) se eliminó a propósito en favor de la app showcase de `src/app`. No lo reintroduzcas a menos que se pida explícitamente. (Nota: el step de `publishInGithubPages.yml` todavía se llama `"Build Storybook"` aunque solo corre `build:lib && build` — es un nombre de step desactualizado, no un retorno de Storybook.)
59
73
60
74
## Gotcha de especificidad CSS al teñir en vivo (distinto del proyecto `table`)
61
75
62
76
La demo de "Theming" inyecta un `<style>` global de forma imperativa vía `Renderer2` + `DOCUMENT` (igual que en el proyecto `table`), porque Angular extrae las etiquetas `<style>` literales de las plantillas en tiempo de compilación y nunca llegan al DOM en tiempo de ejecución.
63
77
64
-
Pero a diferencia de `sau-table` (que usa `ViewEncapsulation.None`), **`DateRangeInputComponent` usa encapsulación Emulated por defecto**. Eso significa que la propia regla `.sau-date-range { ... }` de la librería se compila como `.sau-date-range[_ngcontent-xxx] { ... }` — una clase + un atributo, exactamente la misma especificidad que nuestro override `.theme-live .sau-date-range` (dos clases). Con especificidad empatada, gana el orden de inserción en el `<head>`, que no es fiable (depende de cuándo Angular registra el stylesheet del componente vs. cuándo se ejecuta nuestro constructor). La solución es añadir `!important` a cada declaración generada (función `withImportant()` en `demos.ts`) — confirmado con pruebas, no es una suposición. Si se porta este patrón a otra librería, comprobar primero qué `ViewEncapsulation` usa el componente raíz antes de asumir que la especificidad por selectores basta.
78
+
Pero a diferencia de `sau-table` (que usa `ViewEncapsulation.None`), **`SAUDateRangePickerModule` usa encapsulación Emulated por defecto**. Eso significa que la propia regla `.sau-date-range { ... }` de la librería se compila como `.sau-date-range[_ngcontent-xxx] { ... }` — una clase + un atributo, exactamente la misma especificidad que nuestro override `.theme-live .sau-date-range` (dos clases). Con especificidad empatada, gana el orden de inserción en el `<head>`, que no es fiable (depende de cuándo Angular registra el stylesheet del componente vs. cuándo se ejecuta nuestro constructor). La solución es añadir `!important` a cada declaración generada (función `withImportant()` en `demos.ts`) — confirmado con pruebas, no es una suposición. Si se porta este patrón a otra librería, comprobar primero qué `ViewEncapsulation` usa el componente raíz antes de asumir que la especificidad por selectores basta.
65
79
66
-
Las variables CSS `--sau-color-primary`/`--sau-color-background` en `date-range-input.component.scss` no existían en el componente original — se añadieron expresamente para que la demo de Theming tuviera algo que tocar, siguiendo el mismo patrón que ya usaba `sau-filter`. Solo cubren el acento principal (texto "Rango personalizado...", botón Aplicar, día inicio/fin seleccionado) — los tonos secundarios del hover dentro del calendario quedaron hardcodeados a propósito, igual que en el `sau-filter` original.
80
+
Las variables CSS `--sau-color-primary`/`--sau-color-background` en `date-range-picker.component.scss` no existían en el componente original — se añadieron expresamente para que la demo de Theming tuviera algo que tocar, siguiendo el mismo patrón que ya usaba `sau-filter`. Solo cubren el acento principal (texto "Rango personalizado...", botón Aplicar, día inicio/fin seleccionado) — los tonos secundarios del hover dentro del calendario quedaron hardcodeados a propósito, igual que en el `sau-filter` original.
67
81
68
82
## Cómo funciona el editor de las demos en vivo (`src/app/components/demos`)
69
83
70
-
Mismo patrón que en el proyecto `table`: cada pestaña tiene su propio mini editor de código (`src/app/components/code-editor`) enlazado a un string de configuración (`{ label, placeholder, initialValue?, required? }`, o CSS plano en la pestaña Theming). Al editar (debounce ~600ms), el texto se evalúa con `new Function('"use strict"; return (' + texto + ');')()` — evaluado en el propio navegador del visitante, sin ida y vuelta al servidor (mismo modelo de confianza que cualquier playground de JS).
84
+
Mismo patrón que en el proyecto `table`: cada pestaña tiene su propio mini editor de código (`src/app/components/code-editor`) enlazado a un string de configuración (`{ label, placeholder, initialValue?, required?, dateRangeOptions? }`, o CSS plano en la pestaña Theming). Al editar (debounce ~600ms), el texto se evalúa con `new Function('"use strict"; return (' + texto + ');')()` — evaluado en el propio navegador del visitante, sin ida y vuelta al servidor (mismo modelo de confianza que cualquier playground de JS).
85
+
86
+
`dateRangeOptions` (los presets del dropdown) es un `@Input` del componente — antes era una lista fija interna. La demo "Custom presets" se apoya en esto para mostrar cómo reemplazar los presets en español por una lista propia en inglés (`{ label, value, getRange }`).
71
87
72
-
`DateRangeInputComponent` solo lee el valor inicial de su `formControlItem` dentro del propio setter del `@Input` (no tiene `ngOnChanges`), así que reescribir el valor del mismo `FormControl` con `setValue()` no resetea lo que se ve en pantalla (el calendario no "salta" al nuevo rango). Por eso cada demo sigue usando el mismo truco que `sau-filter`: `@for (cfg of [demo.config()]; track cfg)` — trackear por la referencia del objeto fuerza a Angular a destruir y recrear `<date-range-input>` cada vez que el evaluador produce un objeto nuevo, y el `FormControl` ya tiene el valor correcto seteado (vía `applyJsConfig()`) antes de que eso ocurra.
88
+
`SAUDateRangePickerModule` solo lee el valor inicial de su `formControlItem` dentro del propio setter del `@Input` (no tiene `ngOnChanges`), así que reescribir el valor del mismo `FormControl` con `setValue()` no resetea lo que se ve en pantalla (el calendario no "salta" al nuevo rango). Por eso cada demo sigue usando el mismo truco que `sau-filter`: `@for (cfg of [demo.config()]; track cfg)` — trackear por la referencia del objeto fuerza a Angular a destruir y recrear `<sau-date-range-picker>` cada vez que el evaluador produce un objeto nuevo, y el `FormControl` ya tiene el valor correcto seteado (vía `applyJsConfig()`) antes de que eso ocurra.
73
89
74
90
El rango seleccionado se lee a través de `control.valueChanges` hacia señales (`selectedRange`, `canSubmit`) en vez de depender de que Angular vuelva a marcar el árbol de componentes como "dirty" tras un click dentro de un hijo `OnPush` — es más explícito y no depende de cómo Angular propague la detección de cambios por eventos.
75
91
76
92
## El texto en español dentro de la librería es intencional, no un bug
77
93
78
-
Los presets del dropdown ("Hoy", "Mañana", "Hace 3 días", "Mes actual", "Próximo mes", "Año actual", "Próximo año") y los textos de la UI ("Limpiar rango", "Rango personalizado...", "Aplicar Rango", "Volver") están hardcodeados en español dentro de `date-range-input.component.ts`/`.html`. No es algo que se pueda cambiar desde `label`/`placeholder` ni desde la app showcase — es el comportamiento real del componente. No "corregir" esto en las demos para que parezca todo en inglés; mostrarlo tal cual es lo correcto.
94
+
Los presets del dropdown por defecto ("Hoy", "Ayer", "Hace 3 días", "Mes actual", "Mes anterior", "Año actual", "Año anterior") y los textos de la UI ("Limpiar rango", "Rango personalizado...", "Aplicar Rango", "Volver", "Limpiar") están hardcodeados en español dentro de `date-range-picker.component.ts`/`.html`. No es algo que se pueda cambiar desde `label`/`placeholder` ni desde la app showcase — es el comportamiento real del componente. No "corregir" esto en las demos para que parezca todo en inglés; mostrarlo tal cual es lo correcto. (Los presets sí se pueden *reemplazar* por completo pasando `dateRangeOptions`, como hace la demo "Custom presets" — pero los textos fijos del resto de la UI, sí o sí en español, no.)
95
+
96
+
Además del dropdown de presets y el calendario de doble mes, el input principal admite edición manual: doble click sobre el texto mostrado lo vuelve editable (`isEditingMainInput`, parseo `dd/mm/yyyy - dd/mm/yyyy` vía `onDisplayInputChange`), y el footer del calendario tiene dos `<input type="date">` nativos (`onStartDateInputChange`/`onEndDateInputChange`) para teclear las fechas en vez de hacer click día por día.
79
97
80
98
## Convenciones de este repo (`.github/copilot-instructions.md`)
81
99
82
-
Este repo tiene un archivo de instrucciones para agentes de IA que sí se respetó al escribir los componentes nuevos de `src/app`: `ChangeDetectionStrategy.OnPush` en todos los componentes, `input()`/`output()`/`model()` en vez de decoradores `@Input`/`@Output` donde tiene sentido, `@if`/`@for`/`@switch` nativos en vez de `*ngIf`/`*ngFor`, sin `ngClass`/`ngStyle` (usar `[class.x]`/`[style.x]`), sin arrow functions dentro de plantillas. La librería (`projects/some-angular-utils/date-picker`) en cambio es código preexistente y NO sigue estas convenciones (usa `@Input`/`@Output`, `@HostListener`) — no es necesario migrarla solo por consistencia.
100
+
Este repo tiene un archivo de instrucciones para agentes de IA que sí se respetó al escribir los componentes nuevos de `src/app`: `ChangeDetectionStrategy.OnPush` en todos los componentes, `input()`/`output()`/`model()` en vez de decoradores `@Input`/`@Output` donde tiene sentido, `@if`/`@for`/`@switch` nativos en vez de `*ngIf`/`*ngFor`, sin `ngClass`/`ngStyle` (usar `[class.x]`/`[style.x]`), sin arrow functions dentro de plantillas. La librería (`projects/some-angular-utils/date-range-picker`) en cambio es código preexistente y NO sigue estas convenciones (usa `@Input`/`@Output`, `@HostListener`) — no es necesario migrarla solo por consistencia.
0 commit comments