Skip to content

Commit 1fa01a1

Browse files
committed
First commit
0 parents  commit 1fa01a1

49 files changed

Lines changed: 4706 additions & 0 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.editorconfig

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Editor configuration, see https://editorconfig.org
2+
root = true
3+
4+
[*]
5+
charset = utf-8
6+
indent_style = space
7+
indent_size = 2
8+
insert_final_newline = true
9+
trim_trailing_whitespace = true
10+
11+
[*.ts]
12+
quote_type = single
13+
ij_typescript_use_double_quotes = false
14+
15+
[*.md]
16+
max_line_length = off
17+
trim_trailing_whitespace = false

.github/copilot-instructions.md

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
2+
You are an expert in TypeScript, Angular, and scalable web application development. You write functional, maintainable, performant, and accessible code following Angular and TypeScript best practices.
3+
4+
## TypeScript Best Practices
5+
6+
- Use strict type checking
7+
- Prefer type inference when the type is obvious
8+
- Avoid the `any` type; use `unknown` when type is uncertain
9+
10+
## Angular Best Practices
11+
12+
- Always use standalone components over NgModules
13+
- Must NOT set `standalone: true` inside Angular decorators. It's the default in Angular v20+.
14+
- Use signals for state management
15+
- Implement lazy loading for feature routes
16+
- Do NOT use the `@HostBinding` and `@HostListener` decorators. Put host bindings inside the `host` object of the `@Component` or `@Directive` decorator instead
17+
- Use `NgOptimizedImage` for all static images.
18+
- `NgOptimizedImage` does not work for inline base64 images.
19+
20+
## Accessibility Requirements
21+
22+
- It MUST pass all AXE checks.
23+
- It MUST follow all WCAG AA minimums, including focus management, color contrast, and ARIA attributes.
24+
25+
### Components
26+
27+
- Keep components small and focused on a single responsibility
28+
- Use `input()` and `output()` functions instead of decorators
29+
- Use `computed()` for derived state
30+
- Set `changeDetection: ChangeDetectionStrategy.OnPush` in `@Component` decorator
31+
- Prefer inline templates for small components
32+
- Prefer Reactive forms instead of Template-driven ones
33+
- Do NOT use `ngClass`, use `class` bindings instead
34+
- Do NOT use `ngStyle`, use `style` bindings instead
35+
- When using external templates/styles, use paths relative to the component TS file.
36+
37+
## State Management
38+
39+
- Use signals for local component state
40+
- Use `computed()` for derived state
41+
- Keep state transformations pure and predictable
42+
- Do NOT use `mutate` on signals, use `update` or `set` instead
43+
44+
## Templates
45+
46+
- Keep templates simple and avoid complex logic
47+
- Use native control flow (`@if`, `@for`, `@switch`) instead of `*ngIf`, `*ngFor`, `*ngSwitch`
48+
- Use the async pipe to handle observables
49+
- Do not assume globals like (`new Date()`) are available.
50+
- Do not write arrow functions in templates (they are not supported).
51+
52+
## Services
53+
54+
- Design services around a single responsibility
55+
- Use the `providedIn: 'root'` option for singleton services
56+
- Use the `inject()` function instead of constructor injection

.github/workflows/publish.yml

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
name: Publish Package to npm
2+
3+
on:
4+
release:
5+
types: [published]
6+
7+
permissions:
8+
contents: write
9+
packages: write
10+
11+
jobs:
12+
publish:
13+
runs-on: ubuntu-latest
14+
steps:
15+
- uses: actions/checkout@v4
16+
with:
17+
token: ${{ secrets.GITHUB_TOKEN }}
18+
ref: ${{ github.event.repository.default_branch }}
19+
20+
- name: Setup Node.js
21+
uses: actions/setup-node@v4
22+
with:
23+
node-version: '20.x'
24+
registry-url: 'https://registry.npmjs.org'
25+
26+
- name: Install dependencies
27+
run: npm install --legacy-peer-deps
28+
29+
# - name: Run tests
30+
# run: npm test
31+
32+
- name: copy README.md
33+
run: cp README.md projects/some-angular-utils/date-picker/README.md
34+
35+
- name: build package before version update
36+
run: npm run build:lib
37+
38+
- name: Update package.json version
39+
working-directory: projects/some-angular-utils/date-picker
40+
run: |
41+
TAG="${{ github.event.release.tag_name }}"
42+
npm version "$TAG" --no-git-tag-version
43+
44+
- name: Commit updated package.json
45+
run: |
46+
TAG="${{ github.event.release.tag_name }}"
47+
git config user.name "github-actions"
48+
git config user.email "github-actions@github.com"
49+
git add projects/some-angular-utils/date-picker/package.json
50+
git commit -m "Update package.json to version $TAG"
51+
git push origin ${{ github.event.repository.default_branch }}
52+
env:
53+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
54+
55+
- name: build package with updated version
56+
run: npm run build:lib
57+
58+
- name: Publish to npm
59+
working-directory: dist/some-angular-utils/date-range-picker
60+
run: npm publish --access public
61+
env:
62+
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
63+
64+
- uses: healthplace/npmrc-registry-login-action@v1.0
65+
with:
66+
scope: '@some-angular-utils'
67+
registry: 'https://npm.pkg.github.com'
68+
auth-token: ${{ secrets.GITHUB_TOKEN }}
69+
path: 'dist/some-angular-utils/date-range-picker'
70+
71+
- name: Publish to github npm
72+
working-directory: dist/some-angular-utils/date-range-picker
73+
run: npm publish --access public

.gitignore

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# See https://docs.github.com/get-started/getting-started-with-git/ignoring-files for more about ignoring files.
2+
3+
# Compiled output
4+
/dist
5+
/tmp
6+
/out-tsc
7+
/bazel-out
8+
9+
# Node
10+
/node_modules
11+
npm-debug.log
12+
yarn-error.log
13+
package-lock.json
14+
15+
# IDEs and editors
16+
.idea/
17+
.project
18+
.classpath
19+
.c9/
20+
*.launch
21+
.settings/
22+
*.sublime-workspace
23+
24+
# Visual Studio Code
25+
.vscode/*
26+
!.vscode/settings.json
27+
!.vscode/tasks.json
28+
!.vscode/launch.json
29+
!.vscode/extensions.json
30+
.history/*
31+
32+
# Miscellaneous
33+
/.angular/cache
34+
.sass-cache/
35+
/connect.lock
36+
/coverage
37+
/libpeerconnection.log
38+
testem.log
39+
/typings
40+
__screenshots__/
41+
42+
# System files
43+
.DS_Store
44+
Thumbs.db

.postcssrc.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"plugins": {
3+
"@tailwindcss/postcss": {}
4+
}
5+
}

.vscode/extensions.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=827846
3+
"recommendations": ["angular.ng-template"]
4+
}

.vscode/launch.json

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
3+
"version": "0.2.0",
4+
"configurations": [
5+
{
6+
"name": "ng serve",
7+
"type": "chrome",
8+
"request": "launch",
9+
"preLaunchTask": "npm: start",
10+
"url": "http://localhost:4200/"
11+
},
12+
{
13+
"name": "ng test",
14+
"type": "chrome",
15+
"request": "launch",
16+
"preLaunchTask": "npm: test",
17+
"url": "http://localhost:9876/debug.html"
18+
}
19+
]
20+
}

.vscode/settings.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"workbench.colorCustomizations": {
3+
///////////////////////////////////
4+
"window.activeBorder": "#ff2424",
5+
///////////////////////////////////
6+
"activityBar.background": "#b4b100",
7+
"activityBar.foreground": "#000000",
8+
"activityBar.inactiveForeground": "#414141",
9+
///////////////////////////////////
10+
"titleBar.activeBackground": "#b4b100",
11+
"titleBar.activeForeground": "#000000",
12+
"titleBar.inactiveBackground": "#b4b100",
13+
"titleBar.inactiveForeground": "#000000",
14+
"titleBar.foreground": "#ffffff"
15+
///////////////////////////////////
16+
},
17+
"window.title": "📦 ${rootName} | ${activeEditorShort}${separator}${dirty}"
18+
}

.vscode/tasks.json

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
{
2+
// For more information, visit: https://go.microsoft.com/fwlink/?LinkId=733558
3+
"version": "2.0.0",
4+
"tasks": [
5+
{
6+
"type": "npm",
7+
"script": "start",
8+
"isBackground": true,
9+
"problemMatcher": {
10+
"owner": "typescript",
11+
"pattern": "$tsc",
12+
"background": {
13+
"activeOnStart": true,
14+
"beginsPattern": {
15+
"regexp": "Changes detected"
16+
},
17+
"endsPattern": {
18+
"regexp": "bundle generation (complete|failed)"
19+
}
20+
}
21+
}
22+
},
23+
{
24+
"type": "npm",
25+
"script": "test",
26+
"isBackground": true,
27+
"problemMatcher": {
28+
"owner": "typescript",
29+
"pattern": "$tsc",
30+
"background": {
31+
"activeOnStart": true,
32+
"beginsPattern": {
33+
"regexp": "Changes detected"
34+
},
35+
"endsPattern": {
36+
"regexp": "bundle generation (complete|failed)"
37+
}
38+
}
39+
}
40+
}
41+
]
42+
}

CLAUDE.md

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# CLAUDE.md
2+
3+
Contexto para trabajar en este repo. Es un workspace de Angular con dos proyectos:
4+
5+
- **`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".
7+
8+
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+
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.
11+
12+
## Árbol del código
13+
14+
```
15+
date-input/
16+
├── CLAUDE.md
17+
├── README.md
18+
├── angular.json
19+
├── package.json
20+
├── .postcssrc.json # Tailwind v4 vía @tailwindcss/postcss
21+
├── tsconfig.json # mapea "@some-angular-utils/date-range-picker" -> dist/some-angular-utils/date-range-picker
22+
23+
├── src/ # app showcase (proyecto "date-range-picker")
24+
│ ├── index.html
25+
│ ├── main.ts
26+
│ ├── styles.scss # Tailwind v4 (@import "tailwindcss" + @theme, sin tailwind.config.js)
27+
│ └── app/
28+
│ ├── app.ts / app.html / app.scss / app.config.ts / app.routes.ts
29+
│ └── components/
30+
│ ├── navbar/ navbar.ts — barra superior fija
31+
│ ├── hero/ hero.ts, hero.html — portada con un date-range-input de ejemplo
32+
│ ├── features/ features.ts, features.html — grid de características
33+
│ ├── demos/ demos.ts, demos.html — "See it in action": demos editables en vivo (basic, prefilled, compact, validation, theme)
34+
│ ├── code-editor/ code-editor.ts/html/scss — mini editor de código reutilizable (usado por demos)
35+
│ ├── installation/ installation.ts, installation.html — instrucciones de instalación/uso
36+
│ └── footer/ footer.ts — pie de página
37+
38+
└── projects/some-angular-utils/date-picker/ # la librería publicable (nombre de paquete: date-range-picker)
39+
└── src/
40+
├── public-api.ts # exports públicos del paquete npm (solo DateRangeInputComponent)
41+
└── lib/
42+
└── components/
43+
└── date-range-input/ # único componente publicado: selector de rango de fechas con presets (hoy, mes actual...)
44+
```
45+
46+
## El orden de build importa
47+
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:
49+
50+
```bash
51+
npm run build:lib # ng-packagr -> dist/some-angular-utils/date-range-picker
52+
```
53+
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ó.
55+
56+
## Storybook fue eliminado
57+
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.
59+
60+
## Gotcha de especificidad CSS al teñir en vivo (distinto del proyecto `table`)
61+
62+
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+
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.
65+
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.
67+
68+
## Cómo funciona el editor de las demos en vivo (`src/app/components/demos`)
69+
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).
71+
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.
73+
74+
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+
76+
## El texto en español dentro de la librería es intencional, no un bug
77+
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.
79+
80+
## Convenciones de este repo (`.github/copilot-instructions.md`)
81+
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.
83+
84+
## Tailwind v4
85+
86+
No hay `tailwind.config.js` — v4 se configura con `@import "tailwindcss";` + un bloque `@theme { ... }` directamente en `src/styles.scss`, procesado por `@tailwindcss/postcss` (ver `.postcssrc.json`). La escala de color de marca (`brand-50`...`brand-900`) vive ahí. El IDE puede marcar "Unknown at rule @theme" como advertencia — es solo que el linter no conoce la sintaxis de Tailwind v4, no es un error de build.
87+
88+
## Gotcha de rutas en Windows + git-bash (solo importa al scriptear/probar con la herramienta Bash)
89+
90+
El `/tmp` de git-bash está mapeado a `AppData/Local/Temp`, pero un proceso `node.exe` nativo resuelve un string literal `'/tmp/...'` pasado como argumento JS relativo a la raíz de la unidad actual (`C:\tmp\...`) en su lugar — **no** son el mismo directorio. Si un script de Node escribe archivos en `/tmp/...` y la herramienta Bash no los encuentra después, revisar primero `C:\tmp\...` antes de asumir que la escritura falló.
91+
92+
También: la cwd de la herramienta Bash en esta sesión tiende a resetearse a otro directorio del workspace entre llamadas — antepón siempre `cd "C:/Users/ADMINISTRATOR/Desktop/date-input" &&` a cada comando, no asumas que el `cd` anterior persiste.

0 commit comments

Comments
 (0)