@studiolxd/brand 49.10.0 → 49.12.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 (63) hide show
  1. package/CHANGELOG.md +169 -0
  2. package/dist/_shared/tokens.js +163 -0
  3. package/dist/_types/messages/BrandMessages.d.ts +13 -1
  4. package/dist/_types/messages/index.d.ts +1 -1
  5. package/dist/_types/molecules/ClockWidget/ClockWidget.d.ts +116 -0
  6. package/dist/_types/molecules/ClockWidget/clockDuration.d.ts +23 -0
  7. package/dist/_types/molecules/Heatmap/Heatmap.d.ts +101 -0
  8. package/dist/_types/molecules/Heatmap/heatmapScale.d.ts +39 -0
  9. package/dist/_types/molecules/RecurrenceField/RecurrenceField.d.ts +93 -0
  10. package/dist/_types/molecules/RecurrenceField/recurrenceRule.d.ts +62 -0
  11. package/dist/_types/molecules/SiteNav/SiteNav.d.ts +14 -0
  12. package/dist/_types/molecules/Timeline/Timeline.d.ts +77 -0
  13. package/dist/_types/organisms/OrgChart/OrgChart.d.ts +104 -0
  14. package/dist/_types/organisms/PlanningGrid/PlanningGrid.d.ts +120 -0
  15. package/dist/_types/organisms/PlanningGrid/planningHours.d.ts +10 -0
  16. package/dist/app-launcher.css +1 -1
  17. package/dist/app-launcher.js +7 -4
  18. package/dist/brand.css +402 -4
  19. package/dist/clock-widget.css +1 -0
  20. package/dist/clock-widget.js +126 -0
  21. package/dist/heatmap.css +1 -0
  22. package/dist/heatmap.js +104 -0
  23. package/dist/org-chart.css +1 -0
  24. package/dist/org-chart.js +122 -0
  25. package/dist/planning-grid.css +1 -0
  26. package/dist/planning-grid.js +167 -0
  27. package/dist/recurrence-field.css +1 -0
  28. package/dist/recurrence-field.js +185 -0
  29. package/dist/site-nav.css +1 -1
  30. package/dist/site-nav.js +3 -2
  31. package/dist/timeline.css +1 -0
  32. package/dist/timeline.js +57 -0
  33. package/dist/tokens.css +420 -4
  34. package/package.json +31 -1
  35. package/src/tokens/index.css +6 -0
  36. package/src/tokens/molecules/app-launcher.css +5 -3
  37. package/src/tokens/molecules/clock-widget.css +36 -0
  38. package/src/tokens/molecules/clock-widget.ts +7 -0
  39. package/src/tokens/molecules/heatmap.css +78 -0
  40. package/src/tokens/molecules/heatmap.ts +7 -0
  41. package/src/tokens/molecules/org-chart.css +59 -0
  42. package/src/tokens/molecules/org-chart.ts +7 -0
  43. package/src/tokens/molecules/planning-grid.css +58 -0
  44. package/src/tokens/molecules/planning-grid.ts +7 -0
  45. package/src/tokens/molecules/recurrence-field.css +21 -0
  46. package/src/tokens/molecules/recurrence-field.ts +7 -0
  47. package/src/tokens/molecules/site-nav.css +1 -0
  48. package/src/tokens/molecules/timeline.css +36 -0
  49. package/src/tokens/molecules/timeline.ts +7 -0
  50. package/src/tokens/scss/_index.legacy.scss +6 -0
  51. package/src/tokens/scss/_index.scss +6 -0
  52. package/src/tokens/scss/molecules/_app-launcher.scss +5 -3
  53. package/src/tokens/scss/molecules/_clock-widget.scss +22 -0
  54. package/src/tokens/scss/molecules/_heatmap.scss +48 -0
  55. package/src/tokens/scss/molecules/_org-chart.scss +39 -0
  56. package/src/tokens/scss/molecules/_planning-grid.scss +36 -0
  57. package/src/tokens/scss/molecules/_recurrence-field.scss +11 -0
  58. package/src/tokens/scss/molecules/_site-nav.scss +1 -0
  59. package/src/tokens/scss/molecules/_timeline.scss +22 -0
  60. package/src/tokens/surface-invert.css +56 -0
  61. package/src/tokens/surface-light.css +56 -0
  62. package/src/tokens/surface-public.css +7 -0
  63. package/src/tokens/tokens.json +163 -0
package/CHANGELOG.md CHANGED
@@ -7,6 +7,175 @@ El paquete sigue [semver](https://semver.org/lang/es/): **patch** para bug fixes
7
7
  regeneración de `dist`, **minor** para componentes/props/variantes/tokens nuevos, **major**
8
8
  para breaking changes.
9
9
 
10
+ ## [49.12.0] — 2026-09-19
11
+
12
+ > **Minor.** Las seis piezas que pedían People, Projects y Finance al partir el ERP interno en
13
+ > tres aplicaciones de la suite. Ninguna trae una dependencia nueva: lo que en el ERP hacían
14
+ > `@xyflow/react` + `dagre`, `@tanstack/react-table` y una librería de calendario, aquí lo
15
+ > hacen el CSS, una tabla y treinta líneas de gramática.
16
+
17
+ ### `Timeline` — el historial vertical de estados
18
+
19
+ Un hito por cambio de estado, con su fecha, quien lo firmó y la nota. Es una `<ol>` de verdad,
20
+ así que el orden y el número de hitos se anuncian solos. **No ordena, no interpreta y no
21
+ formatea fechas**: los hitos se pintan en el orden en que llegan, el estado vigente se marca
22
+ con `current` y la fecha llega escrita. El último hito no pinta carril —una línea que muere en
23
+ el aire diría que falta algo por venir— y el estado vigente se **lee** además de pintarse.
24
+
25
+ Subpath `@studiolxd/brand/timeline`. Textos en `timeline`.
26
+
27
+ ### `ClockWidget` — fichar entrada y salida
28
+
29
+ En qué estado está el día, cuánto se lleva trabajado y los tramos ya fichados. **El botón es
30
+ uno y lo decide el turno**: abierto ofrece fichar salida, cerrado ofrece fichar entrada, nunca
31
+ los dos. Sin manejador no pinta botón y el widget se queda de solo lectura, que es lo que se ve
32
+ en la ficha de otra persona.
33
+
34
+ El reloj corre solo —un tic por segundo— mientras haya un tramo abierto, y `now` lo congela:
35
+ es lo que hace que un test y una captura de Chromatic den siempre el mismo resultado. El tiempo
36
+ trabajado va en `role="timer"`, que **no** anuncia cada segundo. En un día de vacaciones, de
37
+ ausencia o no laborable dice por qué y no pinta ni botón ni reloj.
38
+
39
+ La aritmética va aparte y exportada: `clockEntryMinutes`, `clockedMinutes`, `isClockRunning`.
40
+
41
+ Subpath `@studiolxd/brand/clock-widget`. Textos en `clockWidget`, con `durationValue` como
42
+ función de horas y minutos.
43
+
44
+ ### `Heatmap` — la matriz de valores con escala de color
45
+
46
+ Competencias por persona, cobertura de puestos por trimestre. **La cifra va dentro de la
47
+ celda**, así que el color no es nunca la única señal, y la tabla tiene encabezados de verdad en
48
+ los dos ejes (`scope="row"`, `scope="col"`, `scope="colgroup"`).
49
+
50
+ La rampa usa **seis de los siete peldaños** de la rampa secuencial del sistema. El que se queda
51
+ fuera es `sequential-400`: la cifra que va dentro obliga a que cada relleno admita una de las
52
+ dos tintas a 4,5:1, y sobre superficie oscura ese paso no llega con ninguna (4,34:1 con la
53
+ tinta clara, 3,86:1 con la prusia). No se inventa un color para taparlo — regla 9.
54
+
55
+ Una casilla ausente o con `value: null` es **sin dato**, no el mínimo: se pinta rayada sobre la
56
+ superficie secundaria y se anuncia como tal.
57
+
58
+ Subpath `@studiolxd/brand/heatmap`. Textos en `heatmap`. Helpers `heatmapStep`,
59
+ `heatmapRampIndex`, `HEATMAP_RAMP_STEPS`.
60
+
61
+ ### `RecurrenceField` — el editor de recurrencia de un evento
62
+
63
+ Cada cuánto, qué días y hasta cuándo, **enseñando solo lo que hace falta**: sin frecuencia el
64
+ editor es un solo campo, y los días de la semana solo salen en la frecuencia semanal. `null` es
65
+ «no se repite», que no es una repetición con los campos a cero.
66
+
67
+ `buildRecurrenceRule` y `parseRecurrenceRule` escriben y leen el subconjunto de RFC 5545 que el
68
+ editor expone (`FREQ`, `INTERVAL`, `BYDAY`, `UNTIL`/`COUNT`). **Lo que el editor no enseña se
69
+ conserva**: `BYMONTHDAY`, `WKST` y demás se apartan al leer y se vuelven a escribir, así que
70
+ editar la repetición no destruye lo que no se ve. `UNTIL` llega al final del día en UTC.
71
+
72
+ Sin dependencia de calendario: lo que un campo de formulario necesita son treinta líneas de
73
+ gramática, y una librería entera la pagarían las once aplicaciones que consumen el paquete.
74
+
75
+ Los **días salen del `locale`** con `Intl`, no del catálogo de textos.
76
+
77
+ Subpath `@studiolxd/brand/recurrence-field`. Textos en `recurrenceField`.
78
+
79
+ ### `PlanningGrid` — la rejilla editable de horas
80
+
81
+ Fila × columna —proyecto por semana, persona por día—, con los totales de cada eje y la
82
+ disponibilidad de cada columna al pie. No sabe qué hay en cada eje: es la misma pieza para la
83
+ planificación semanal y para la dedicación diaria.
84
+
85
+ `onCellChange` se llama al **confirmar** un cruce —al salir del campo o con Intro—, nunca en
86
+ cada tecla. **El estado en vuelo y el error viajan en la propia celda** (`pending`, `error`),
87
+ que es lo que permite dos cruces en vuelo a la vez sin que la rejilla lleve la cuenta. Lo que no
88
+ se edita es **texto**, no un campo deshabilitado. Pasarse de la disponibilidad se pinta en el
89
+ color de error **y se dice**.
90
+
91
+ El campo admite coma y punto decimal; `parsePlanningHours` está exportado.
92
+
93
+ Subpath `@studiolxd/brand/planning-grid`. Textos en `planningGrid`, con `cellLabel` como
94
+ función de los dos ejes.
95
+
96
+ ### `OrgChart` — el organigrama de departamentos
97
+
98
+ Quién responde de cada uno, quién está dentro y de quién cuelga. Plegable, con zoom y con
99
+ desplazamiento.
100
+
101
+ **Es un árbol de listas anidadas, no un lienzo de dibujo**: las líneas las pintan
102
+ pseudoelementos, así que el orden del documento es el del árbol y un lector de pantalla recorre
103
+ la jerarquía de verdad, sin necesidad de una alternativa en texto escrita aparte. No hay motor
104
+ de disposición que mantener: un árbol se dibuja centrando cada nodo sobre sus hijos, y eso lo
105
+ hace el CSS solo.
106
+
107
+ El zoom usa **`zoom` y no `transform: scale()`**, porque `zoom` rehace la maqueta: el lienzo
108
+ sigue sabiendo cuánto mide lo que tiene dentro y el desplazamiento llega justo al final. El
109
+ valor lo escribe el CSSOM (`useCssProperties`), no un atributo `style`. Una rama plegada **no
110
+ se renderiza**, para no dejar cientos de nodos en el árbol de accesibilidad y en el tabulador.
111
+
112
+ Subpath `@studiolxd/brand/org-chart`. Textos en `orgChart`, con `collapse`/`expand` como
113
+ funciones del nombre del departamento.
114
+
115
+ ### Tokens
116
+
117
+ Seis conjuntos nuevos: `tokens/molecule/timeline.json`, `clock-widget.json`, `heatmap.json`,
118
+ `recurrence-field.json` y `tokens/organism/planning-grid.json`, `org-chart.json`. Todos con sus
119
+ pares `surface-dark-*` por la regla de derivación. Ningún color nuevo en `tokens/color/`.
120
+
121
+ ### Internacionalización
122
+
123
+ Seis espacios nuevos en `BrandMessages`: `timeline`, `clockWidget`, `heatmap`,
124
+ `recurrenceField`, `planningGrid` y `orgChart`. **Una aplicación que no los rellene no
125
+ compila**, que es el contrato de siempre. La tabla de Foundations › Internacionalización queda
126
+ actualizada.
127
+
128
+ ## [49.11.0] — 2026-09-19
129
+
130
+ > **Minor.** Tres decisiones de maqueta sobre lo que la 49.10.0 dejó puesto: dónde va el
131
+ > distintivo en el lanzador, que su panel crezca en vez de desplazarse, y que un grupo largo
132
+ > del índice público quepa a dos columnas bajo un solo título.
133
+
134
+ ### El distintivo va encima del nombre, y su hueco se reserva siempre
135
+
136
+ En `AppLauncher`, el distintivo de la baldosa se pinta **sobre** el nombre de la app, no a su
137
+ lado. Y su fila se pinta **siempre**, lleve distintivo la baldosa o no: vacía es el hueco
138
+ reservado que deja todos los nombres de la rejilla a la misma altura, de modo que una app que
139
+ estrena píldora no empuja hacia abajo el nombre de su vecina.
140
+
141
+ El alto de esa fila es el de una `Tag` y sale de sus tokens —
142
+ `app-launcher.tile-badge-block-size` = `calc({tag.font-size} * {tag.line-height} + 2 *
143
+ {tag.padding-block})` —, así que sigue cuadrando si la píldora cambia de talla. No hay medida
144
+ escrita a mano.
145
+
146
+ ### El panel del lanzador crece con su contenido
147
+
148
+ El panel **no tiene tope propio**: con las doce aplicaciones de la suite se ve entero. El único
149
+ límite es el hueco que de verdad queda en la ventana (`--available-height`, que mide Base UI y
150
+ que `app-launcher.content-max-height` no hace más que nombrar); solo cuando ni ese hueco da, el
151
+ panel se recorta ahí y hace scroll. Un tope propio —una altura fija, «tantas filas»— recortaría
152
+ la rejilla teniendo pantalla de sobra, y ahora lo impide un guardián sobre la hoja
153
+ (`AppLauncher.panel.test.ts`).
154
+
155
+ La rejilla pasa a **dos columnas en un panel estrecho** y tres desde `sm`
156
+ (`app-launcher.content-columns-narrow`, nuevo, junto al `content-columns` de siempre): con tres
157
+ columnas en móvil, doce baldosas dejaban los nombres partidos en tres líneas.
158
+
159
+ ### `SiteNav`: un grupo a dos columnas, bajo un solo título
160
+
161
+ `SiteNavGroup` gana **`columns?: 1 | 2`** (por defecto `1`, el grupo de siempre). Con `2` el
162
+ grupo ocupa en la rejilla el **ancho de dos** —doce tramos en `md`, ocho en `lg`, seis en `xl`—
163
+ y reparte sus ítems en **dos columnas bajo un solo título**. Es lo que da sitio a un grupo largo
164
+ —«Aplicaciones», con las diez o doce de la suite— sin partirlo en dos grupos con dos cabeceras,
165
+ que sería otro índice.
166
+
167
+ - El pasillo entre esas dos columnas es el mismo que separa dos grupos
168
+ (`site-nav.item-column-gap`, nuevo, que apunta a `group-gap`): más estrecho, el grupo ancho se
169
+ leería como dos grupos pegados.
170
+ - Los ítems se leen **por filas**, en el orden del DOM.
171
+ - **Por debajo de `md` vuelve a una columna**, como todo el índice.
172
+ - El grupo ancho cuenta **por dos** en el tope de columnas (`site-nav.columns-max`) y queda
173
+ fuera del reparto de la última fila: ya ocupa lo que le toca y no tiene sobrante que recoger.
174
+ - `disabled` y `badge` de la 49.10.0 siguen valiendo dentro de las dos columnas.
175
+
176
+ El dato viaja en `data-group-columns`, nunca en un atributo `style` — el mismo motivo que
177
+ `data-columns`.
178
+
10
179
  ## [49.10.0] — 2026-09-18
11
180
 
12
181
  > **Minor.** La suite marca productos como «próximamente»: se ven en su sitio pero apagados,
@@ -193,6 +193,7 @@ var e = {
193
193
  "--app-launcher-trigger-label-color": "#111e30",
194
194
  "--app-launcher-content-min-width": "20rem",
195
195
  "--app-launcher-content-columns": "3",
196
+ "--app-launcher-content-columns-narrow": "2",
196
197
  "--app-launcher-content-gap": "0.25rem",
197
198
  "--app-launcher-content-bg": "#ffffff",
198
199
  "--app-launcher-content-border-width": "1px",
@@ -208,6 +209,7 @@ var e = {
208
209
  "--app-launcher-tile-padding-block": "0.5rem",
209
210
  "--app-launcher-tile-padding-inline": "1rem",
210
211
  "--app-launcher-tile-gap": "0.25rem",
212
+ "--app-launcher-tile-badge-block-size": "calc(0.875rem * 1 + 2 * 0.25rem)",
211
213
  "--app-launcher-tile-border-radius": "0px",
212
214
  "--app-launcher-tile-name-font-size": "0.875rem",
213
215
  "--app-launcher-tile-name-font-weight": "300",
@@ -2348,6 +2350,25 @@ var e = {
2348
2350
  "--checkbox-field-helper-font-weight": "300",
2349
2351
  "--checkbox-field-helper-line-height": "1.5",
2350
2352
  "--checkbox-field-helper-color": "#111e30",
2353
+ "--clock-widget-font-family": "\"Google Sans Flex\", system-ui, sans-serif",
2354
+ "--clock-widget-gap": "1rem",
2355
+ "--clock-widget-header-gap": "0.25rem",
2356
+ "--clock-widget-date-font-size": "0.875rem",
2357
+ "--clock-widget-date-color": "#4a4a4a",
2358
+ "--clock-widget-status-gap": "0.5rem",
2359
+ "--clock-widget-status-font-size": "0.875rem",
2360
+ "--clock-widget-status-color": "#4a4a4a",
2361
+ "--clock-widget-status-dot-size": "0.5rem",
2362
+ "--clock-widget-status-dot-radius": "9999px",
2363
+ "--clock-widget-status-dot-open-bg": "#006616",
2364
+ "--clock-widget-status-dot-closed-bg": "#d0d0d0",
2365
+ "--clock-widget-elapsed-font-size": "2rem",
2366
+ "--clock-widget-elapsed-font-weight": "500",
2367
+ "--clock-widget-elapsed-line-height": "1.1",
2368
+ "--clock-widget-elapsed-color": "#111e30",
2369
+ "--clock-widget-elapsed-label-font-size": "0.875rem",
2370
+ "--clock-widget-elapsed-label-color": "#4a4a4a",
2371
+ "--clock-widget-footer-margin-block-start": "0.5rem",
2351
2372
  "--command-palette-max-height": "min(24rem, 60vh)",
2352
2373
  "--command-palette-input-padding-block": "0.75rem",
2353
2374
  "--command-palette-input-padding-inline": "0.5rem",
@@ -2561,6 +2582,51 @@ var e = {
2561
2582
  "--filter-bar-label-offset": "calc(0.875rem * 1.5 + 0.5rem)",
2562
2583
  "--filter-bar-control-block-size": "2.5rem",
2563
2584
  "--form-field-gap": "0.25rem",
2585
+ "--heatmap-font-family": "\"Google Sans Flex\", system-ui, sans-serif",
2586
+ "--heatmap-radius": "0px",
2587
+ "--heatmap-border-color": "#111e30",
2588
+ "--heatmap-border-width": "1px",
2589
+ "--heatmap-row-header-width": "10rem",
2590
+ "--heatmap-row-header-font-size": "0.875rem",
2591
+ "--heatmap-row-header-color": "#111e30",
2592
+ "--heatmap-row-header-font-weight": "300",
2593
+ "--heatmap-row-header-bg": "#ffffff",
2594
+ "--heatmap-row-header-padding-block": "0.5rem",
2595
+ "--heatmap-row-header-padding-inline": "0.75rem",
2596
+ "--heatmap-column-header-font-size": "0.75rem",
2597
+ "--heatmap-column-header-font-weight": "500",
2598
+ "--heatmap-column-header-color": "#4a4a4a",
2599
+ "--heatmap-column-header-bg": "#ffffff",
2600
+ "--heatmap-column-header-padding-block": "0.5rem",
2601
+ "--heatmap-column-header-padding-inline": "0.5rem",
2602
+ "--heatmap-column-header-width": "3rem",
2603
+ "--heatmap-group-header-font-size": "0.75rem",
2604
+ "--heatmap-group-header-color": "#4a4a4a",
2605
+ "--heatmap-cell-padding-block": "0.5rem",
2606
+ "--heatmap-cell-padding-inline": "0.5rem",
2607
+ "--heatmap-cell-font-size": "0.875rem",
2608
+ "--heatmap-cell-font-weight": "500",
2609
+ "--heatmap-empty-bg": "#f2f2f2",
2610
+ "--heatmap-empty-pattern-color": "#d0d0d0",
2611
+ "--heatmap-empty-pattern-width": "0.25rem",
2612
+ "--heatmap-legend-gap": "0.5rem",
2613
+ "--heatmap-legend-font-size": "0.875rem",
2614
+ "--heatmap-legend-color": "#4a4a4a",
2615
+ "--heatmap-legend-swatch-size": "0.75rem",
2616
+ "--heatmap-legend-swatch-gap": "0.25rem",
2617
+ "--heatmap-margin-block-start": "0.75rem",
2618
+ "--heatmap-step-1-bg": "#dce9fc",
2619
+ "--heatmap-step-1-color": "#111e30",
2620
+ "--heatmap-step-2-bg": "#b6d2f8",
2621
+ "--heatmap-step-2-color": "#111e30",
2622
+ "--heatmap-step-3-bg": "#8fbaf5",
2623
+ "--heatmap-step-3-color": "#111e30",
2624
+ "--heatmap-step-4-bg": "#3b88ee",
2625
+ "--heatmap-step-4-color": "#111e30",
2626
+ "--heatmap-step-5-bg": "#236ac5",
2627
+ "--heatmap-step-5-color": "#ffffff",
2628
+ "--heatmap-step-6-bg": "#164b90",
2629
+ "--heatmap-step-6-color": "#ffffff",
2564
2630
  "--image-crop-dialog-gap": "1rem",
2565
2631
  "--image-crop-dialog-area-max-height": "60vh",
2566
2632
  "--image-crop-dialog-area-min-height": "60vh",
@@ -2822,6 +2888,14 @@ var e = {
2822
2888
  "--recovery-codes-line-height": "1.65",
2823
2889
  "--recovery-codes-color": "#111e30",
2824
2890
  "--recovery-codes-index-color": "#4a4a4a",
2891
+ "--recurrence-field-gap": "1rem",
2892
+ "--recurrence-field-row-gap": "0.75rem",
2893
+ "--recurrence-field-interval-width": "8rem",
2894
+ "--recurrence-field-weekdays-gap": "0.25rem",
2895
+ "--recurrence-field-legend-font-size": "0.875rem",
2896
+ "--recurrence-field-legend-color": "#111e30",
2897
+ "--recurrence-field-legend-font-weight": "500",
2898
+ "--recurrence-field-legend-margin-block-end": "0.25rem",
2825
2899
  "--search-form-inline-size": "100%",
2826
2900
  "--search-form-xl-height": "4rem",
2827
2901
  "--search-form-xl-padding-inline": "2rem",
@@ -2891,6 +2965,7 @@ var e = {
2891
2965
  "--site-nav-item-hover-line-width": "1px",
2892
2966
  "--site-nav-item-disabled-color": "#4a4a4a",
2893
2967
  "--site-nav-item-badge-gap": "0.5rem",
2968
+ "--site-nav-item-column-gap": "2rem",
2894
2969
  "--site-nav-item-current-font-weight": "300",
2895
2970
  "--site-nav-focus-ring-width": "2px",
2896
2971
  "--site-nav-focus-ring-offset": "2px",
@@ -3027,6 +3102,25 @@ var e = {
3027
3102
  "--time-field-helper-font-weight": "300",
3028
3103
  "--time-field-helper-line-height": "1.5",
3029
3104
  "--time-field-helper-color": "#111e30",
3105
+ "--timeline-font-family": "\"Google Sans Flex\", system-ui, sans-serif",
3106
+ "--timeline-gap": "1.5rem",
3107
+ "--timeline-column-gap": "0.75rem",
3108
+ "--timeline-rail-width": "1px",
3109
+ "--timeline-rail-color": "#d0d0d0",
3110
+ "--timeline-rail-gap": "0.25rem",
3111
+ "--timeline-title-font-size": "1rem",
3112
+ "--timeline-title-font-weight": "500",
3113
+ "--timeline-title-line-height": "1.3",
3114
+ "--timeline-title-color": "#111e30",
3115
+ "--timeline-meta-font-size": "0.875rem",
3116
+ "--timeline-meta-color": "#4a4a4a",
3117
+ "--timeline-meta-gap": "0.5rem",
3118
+ "--timeline-meta-separator-color": "#d0d0d0",
3119
+ "--timeline-note-font-size": "1rem",
3120
+ "--timeline-note-line-height": "1.5",
3121
+ "--timeline-note-color": "#111e30",
3122
+ "--timeline-body-gap": "0.25rem",
3123
+ "--timeline-actions-margin-block-start": "0.5rem",
3030
3124
  "--tree-view-icon-size": "16px",
3031
3125
  "--tree-view-indent": "calc(16px + 0.5rem)",
3032
3126
  "--tree-view-row-gap": "0.5rem",
@@ -3239,6 +3333,75 @@ var e = {
3239
3333
  "--onboarding-shell-actions-gap": "1rem",
3240
3334
  "--onboarding-shell-actions-margin-block-start": "2rem",
3241
3335
  "--onboarding-shell-actions-padding-block-start": "1.5rem",
3336
+ "--org-chart-font-family": "\"Google Sans Flex\", system-ui, sans-serif",
3337
+ "--org-chart-gap": "0.75rem",
3338
+ "--org-chart-viewport-block-size": "32rem",
3339
+ "--org-chart-viewport-padding": "1.5rem",
3340
+ "--org-chart-viewport-bg": "#f2f2f2",
3341
+ "--org-chart-viewport-radius": "0px",
3342
+ "--org-chart-viewport-border-color": "#d0d0d0",
3343
+ "--org-chart-viewport-border-width": "1px",
3344
+ "--org-chart-focus-ring-color": "#111e30",
3345
+ "--org-chart-focus-ring-width": "2px",
3346
+ "--org-chart-focus-ring-offset": "2px",
3347
+ "--org-chart-sibling-gap": "1rem",
3348
+ "--org-chart-connector-length": "2rem",
3349
+ "--org-chart-connector-color": "#d0d0d0",
3350
+ "--org-chart-connector-width": "1px",
3351
+ "--org-chart-card-inline-size": "14rem",
3352
+ "--org-chart-card-bg": "#ffffff",
3353
+ "--org-chart-card-border-color": "#111e30",
3354
+ "--org-chart-card-border-width": "1px",
3355
+ "--org-chart-card-radius": "0px",
3356
+ "--org-chart-card-padding-block": "0.5rem",
3357
+ "--org-chart-card-padding-inline": "0.75rem",
3358
+ "--org-chart-card-gap": "0.5rem",
3359
+ "--org-chart-name-font-size": "0.875rem",
3360
+ "--org-chart-name-font-weight": "500",
3361
+ "--org-chart-name-color": "#111e30",
3362
+ "--org-chart-group-font-size": "0.75rem",
3363
+ "--org-chart-group-color": "#4a4a4a",
3364
+ "--org-chart-group-gap": "0.25rem",
3365
+ "--org-chart-group-margin-block-start": "0.5rem",
3366
+ "--org-chart-person-font-size": "0.875rem",
3367
+ "--org-chart-person-color": "#111e30",
3368
+ "--org-chart-person-role-font-size": "0.75rem",
3369
+ "--org-chart-person-role-color": "#4a4a4a",
3370
+ "--org-chart-person-gap": "0.25rem",
3371
+ "--org-chart-empty-color": "#4a4a4a",
3372
+ "--planning-grid-font-family": "\"Google Sans Flex\", system-ui, sans-serif",
3373
+ "--planning-grid-radius": "0px",
3374
+ "--planning-grid-border-color": "#111e30",
3375
+ "--planning-grid-border-width": "1px",
3376
+ "--planning-grid-row-header-width": "12rem",
3377
+ "--planning-grid-row-header-font-size": "0.875rem",
3378
+ "--planning-grid-row-header-font-weight": "300",
3379
+ "--planning-grid-row-header-color": "#111e30",
3380
+ "--planning-grid-row-header-bg": "#ffffff",
3381
+ "--planning-grid-row-header-padding-block": "0.5rem",
3382
+ "--planning-grid-row-header-padding-inline": "0.75rem",
3383
+ "--planning-grid-column-header-font-size": "0.875rem",
3384
+ "--planning-grid-column-header-font-weight": "500",
3385
+ "--planning-grid-column-header-color": "#111e30",
3386
+ "--planning-grid-column-header-sub-font-size": "0.75rem",
3387
+ "--planning-grid-column-header-sub-color": "#4a4a4a",
3388
+ "--planning-grid-column-header-bg": "#ffffff",
3389
+ "--planning-grid-column-header-padding-block": "0.5rem",
3390
+ "--planning-grid-column-width": "6rem",
3391
+ "--planning-grid-current-outline-color": "#111e30",
3392
+ "--planning-grid-current-outline-width": "2px",
3393
+ "--planning-grid-cell-padding-block": "0.25rem",
3394
+ "--planning-grid-cell-padding-inline": "0.25rem",
3395
+ "--planning-grid-cell-font-size": "0.875rem",
3396
+ "--planning-grid-cell-color": "#111e30",
3397
+ "--planning-grid-cell-readonly-color": "#4a4a4a",
3398
+ "--planning-grid-cell-readonly-bg": "#f2f2f2",
3399
+ "--planning-grid-cell-input-width": "4.5rem",
3400
+ "--planning-grid-footer-bg": "#f2f2f2",
3401
+ "--planning-grid-footer-font-weight": "500",
3402
+ "--planning-grid-footer-color": "#111e30",
3403
+ "--planning-grid-over-color": "#b30000",
3404
+ "--planning-grid-pending-opacity": "0.4",
3242
3405
  "--public-page-shell-preferences-padding-block": "1.5rem",
3243
3406
  "--public-page-shell-preferences-gap": "0.75rem",
3244
3407
  "--steps-gap": "1.5rem",
@@ -52,6 +52,12 @@ import type { ProgressBarMessages } from '../atoms/ProgressBar/ProgressBar';
52
52
  import type { SpinnerMessages } from '../atoms/Spinner/Spinner';
53
53
  import type { SliderMessages } from '../atoms/Slider/Slider';
54
54
  import type { TreeViewMessages } from '../molecules/TreeView/TreeView';
55
+ import type { ClockWidgetMessages } from '../molecules/ClockWidget/ClockWidget';
56
+ import type { HeatmapMessages } from '../molecules/Heatmap/Heatmap';
57
+ import type { OrgChartMessages } from '../organisms/OrgChart/OrgChart';
58
+ import type { PlanningGridMessages } from '../organisms/PlanningGrid/PlanningGrid';
59
+ import type { RecurrenceFieldMessages } from '../molecules/RecurrenceField/RecurrenceField';
60
+ import type { TimelineMessages } from '../molecules/Timeline/Timeline';
55
61
  import type { UptimeBarsMessages } from '../molecules/UptimeBars/UptimeBars';
56
62
  import type { ChartMessages } from '../organisms/Chart/Chart';
57
63
  import type { StepperMessages } from '../molecules/Stepper/Stepper';
@@ -150,6 +156,12 @@ export interface BrandMessages {
150
156
  spinner: SpinnerMessages;
151
157
  slider: SliderMessages;
152
158
  treeView: TreeViewMessages;
159
+ clockWidget: ClockWidgetMessages;
160
+ heatmap: HeatmapMessages;
161
+ orgChart: OrgChartMessages;
162
+ planningGrid: PlanningGridMessages;
163
+ recurrenceField: RecurrenceFieldMessages;
164
+ timeline: TimelineMessages;
153
165
  uptimeBars: UptimeBarsMessages;
154
166
  chart: ChartMessages;
155
167
  stepper: StepperMessages;
@@ -173,4 +185,4 @@ export interface BrandMessages {
173
185
  connectorExternalSignIn: ConnectorExternalSignInMessages;
174
186
  connectorRejection: ConnectorRejectionMessages;
175
187
  }
176
- export type { PaginationMessages, TableMessages, DataTableMessages, InputFieldMessages, PasswordFieldMessages, SelectMessages, MultiSelectMessages, NumberInputMessages, OtpInputMessages, InputPhoneMessages, AsyncSelectMessages, AsyncMultiSelectMessages, DocsSearchMessages, SearchFormMessages, FilterBarMessages, CalendarMessages, DatePickerMessages, TimeSelectMessages, FileUploadMessages, ImageCropDialogMessages, AvatarUploadMessages, ModalMessages, SheetMessages, ConfirmDialogMessages, AlertMessages, BannerMessages, ToasterMessages, ConsentMessages, CommandPaletteMessages, AppLauncherMessages, FloatingDockMessages, NotificationButtonMessages, NotificationPanelMessages, MenuButtonMessages, AppRootMessages, AppShellMessages, SidebarMessages, SidebarNavMessages, SiteNavMessages, SiteHeaderMessages, UserMenuMessages, OrgSwitcherMessages, BreadcrumbMessages, TableOfContentsMessages, PrevNextNavMessages, PublicPageShellMessages, OnboardingShellMessages, CopyMessages, CodeBlockMessages, DotsButtonMessages, ProgressBarMessages, SpinnerMessages, SliderMessages, TreeViewMessages, UptimeBarsMessages, ChartMessages, StepperMessages, CarouselMessages, LanguageSwitcherMessages, ProjectCardMessages, LegalFooterMessages, CalendarRosterMessages, CalendarPlannerMessages, NotificationListMessages, MessageComposerMessages, ConversationListMessages, ConversationThreadMessages, TypingIndicatorMessages, AnnotationThreadMessages, ChatShellMessages, UntrustedTextMessages, ConnectorRequestSummaryMessages, ConnectorConsentMessages, ConnectorSignInMessages, ConnectorExternalSignInMessages, ConnectorRejectionMessages, };
188
+ export type { PaginationMessages, TableMessages, DataTableMessages, InputFieldMessages, PasswordFieldMessages, SelectMessages, MultiSelectMessages, NumberInputMessages, OtpInputMessages, InputPhoneMessages, AsyncSelectMessages, AsyncMultiSelectMessages, DocsSearchMessages, SearchFormMessages, FilterBarMessages, CalendarMessages, DatePickerMessages, TimeSelectMessages, FileUploadMessages, ImageCropDialogMessages, AvatarUploadMessages, ModalMessages, SheetMessages, ConfirmDialogMessages, AlertMessages, BannerMessages, ToasterMessages, ConsentMessages, CommandPaletteMessages, AppLauncherMessages, FloatingDockMessages, NotificationButtonMessages, NotificationPanelMessages, MenuButtonMessages, AppRootMessages, AppShellMessages, SidebarMessages, SidebarNavMessages, SiteNavMessages, SiteHeaderMessages, UserMenuMessages, OrgSwitcherMessages, BreadcrumbMessages, TableOfContentsMessages, PrevNextNavMessages, PublicPageShellMessages, OnboardingShellMessages, CopyMessages, CodeBlockMessages, DotsButtonMessages, ProgressBarMessages, SpinnerMessages, SliderMessages, TreeViewMessages, ClockWidgetMessages, HeatmapMessages, OrgChartMessages, PlanningGridMessages, RecurrenceFieldMessages, TimelineMessages, UptimeBarsMessages, ChartMessages, StepperMessages, CarouselMessages, LanguageSwitcherMessages, ProjectCardMessages, LegalFooterMessages, CalendarRosterMessages, CalendarPlannerMessages, NotificationListMessages, MessageComposerMessages, ConversationListMessages, ConversationThreadMessages, TypingIndicatorMessages, AnnotationThreadMessages, ChatShellMessages, UntrustedTextMessages, ConnectorRequestSummaryMessages, ConnectorConsentMessages, ConnectorSignInMessages, ConnectorExternalSignInMessages, ConnectorRejectionMessages, };
@@ -2,4 +2,4 @@ export { BrandMessagesProvider } from './BrandMessagesProvider';
2
2
  export type { BrandMessagesProviderProps } from './BrandMessagesProvider';
3
3
  export { useBrandMessages } from './BrandMessagesContext';
4
4
  export type { BrandMessagesReader } from './BrandMessagesContext';
5
- export type { BrandMessages, PaginationMessages, TableMessages, DataTableMessages, InputFieldMessages, PasswordFieldMessages, SelectMessages, MultiSelectMessages, NumberInputMessages, OtpInputMessages, InputPhoneMessages, AsyncSelectMessages, AsyncMultiSelectMessages, DocsSearchMessages, SearchFormMessages, FilterBarMessages, CalendarMessages, DatePickerMessages, TimeSelectMessages, FileUploadMessages, ImageCropDialogMessages, AvatarUploadMessages, ModalMessages, SheetMessages, ConfirmDialogMessages, AlertMessages, BannerMessages, ToasterMessages, ConsentMessages, CommandPaletteMessages, AppLauncherMessages, FloatingDockMessages, NotificationButtonMessages, NotificationPanelMessages, MenuButtonMessages, AppRootMessages, AppShellMessages, SidebarMessages, SidebarNavMessages, SiteNavMessages, SiteHeaderMessages, UserMenuMessages, OrgSwitcherMessages, BreadcrumbMessages, TableOfContentsMessages, PrevNextNavMessages, PublicPageShellMessages, OnboardingShellMessages, CopyMessages, CodeBlockMessages, DotsButtonMessages, ProgressBarMessages, SpinnerMessages, SliderMessages, TreeViewMessages, UptimeBarsMessages, ChartMessages, StepperMessages, CarouselMessages, LanguageSwitcherMessages, ProjectCardMessages, LegalFooterMessages, CalendarRosterMessages, CalendarPlannerMessages, NotificationListMessages, MessageComposerMessages, ConversationListMessages, ConversationThreadMessages, TypingIndicatorMessages, AnnotationThreadMessages, ChatShellMessages, UntrustedTextMessages, ConnectorRequestSummaryMessages, ConnectorConsentMessages, ConnectorSignInMessages, ConnectorExternalSignInMessages, ConnectorRejectionMessages, } from './BrandMessages';
5
+ export type { BrandMessages, PaginationMessages, TableMessages, DataTableMessages, InputFieldMessages, PasswordFieldMessages, SelectMessages, MultiSelectMessages, NumberInputMessages, OtpInputMessages, InputPhoneMessages, AsyncSelectMessages, AsyncMultiSelectMessages, DocsSearchMessages, SearchFormMessages, FilterBarMessages, CalendarMessages, DatePickerMessages, TimeSelectMessages, FileUploadMessages, ImageCropDialogMessages, AvatarUploadMessages, ModalMessages, SheetMessages, ConfirmDialogMessages, AlertMessages, BannerMessages, ToasterMessages, ConsentMessages, CommandPaletteMessages, AppLauncherMessages, FloatingDockMessages, NotificationButtonMessages, NotificationPanelMessages, MenuButtonMessages, AppRootMessages, AppShellMessages, SidebarMessages, SidebarNavMessages, SiteNavMessages, SiteHeaderMessages, UserMenuMessages, OrgSwitcherMessages, BreadcrumbMessages, TableOfContentsMessages, PrevNextNavMessages, PublicPageShellMessages, OnboardingShellMessages, CopyMessages, CodeBlockMessages, DotsButtonMessages, ProgressBarMessages, SpinnerMessages, SliderMessages, TreeViewMessages, ClockWidgetMessages, HeatmapMessages, OrgChartMessages, PlanningGridMessages, RecurrenceFieldMessages, TimelineMessages, UptimeBarsMessages, ChartMessages, StepperMessages, CarouselMessages, LanguageSwitcherMessages, ProjectCardMessages, LegalFooterMessages, CalendarRosterMessages, CalendarPlannerMessages, NotificationListMessages, MessageComposerMessages, ConversationListMessages, ConversationThreadMessages, TypingIndicatorMessages, AnnotationThreadMessages, ChatShellMessages, UntrustedTextMessages, ConnectorRequestSummaryMessages, ConnectorConsentMessages, ConnectorSignInMessages, ConnectorExternalSignInMessages, ConnectorRejectionMessages, } from './BrandMessages';
@@ -0,0 +1,116 @@
1
+ import { type ComponentPropsWithoutRef, type ReactNode } from 'react';
2
+ import { type ClockEntry } from './clockDuration';
3
+ import './ClockWidget.css';
4
+ export type { ClockEntry } from './clockDuration';
5
+ /**
6
+ * El estado del día. `working` es el único en el que se ficha: en los otros
7
+ * tres el widget enseña por qué no hay nada que fichar y no pinta el botón.
8
+ */
9
+ export type ClockWidgetDayState = 'working' | 'non-working' | 'vacation' | 'absence';
10
+ /**
11
+ * Los textos del fichaje. Todo es **cromo**: el título del widget, los dos
12
+ * botones, los encabezados de la tabla de tramos y cómo se nombran los tres
13
+ * días en los que no se ficha. Las horas no pasan por aquí —son formato, y
14
+ * salen de `locale` con `Intl`—, pero la **duración** sí: «2 h 14 min» se
15
+ * escribe distinto en cada idioma, así que es una función del catálogo.
16
+ */
17
+ export interface ClockWidgetMessages {
18
+ /** Título del widget. */
19
+ title: string;
20
+ /** Botón que abre el turno. */
21
+ clockIn: string;
22
+ /** Botón que lo cierra. */
23
+ clockOut: string;
24
+ /** Lo que dice el botón mientras la acción está en vuelo. */
25
+ pending: string;
26
+ /** Etiqueta de la cifra grande: el tiempo trabajado hoy. */
27
+ elapsed: string;
28
+ /** Nombre accesible de la tabla de tramos. */
29
+ entries: string;
30
+ /** Encabezado de la columna de entrada. */
31
+ in: string;
32
+ /** Encabezado de la columna de salida. */
33
+ out: string;
34
+ /** Encabezado de la columna de duración. */
35
+ duration: string;
36
+ /** Lo que se pone en la salida del tramo todavía abierto. */
37
+ running: string;
38
+ /** Etiqueta de la fila de total. */
39
+ total: string;
40
+ /** Un día no laborable. */
41
+ nonWorking: string;
42
+ /** Un día de vacaciones. */
43
+ vacation: string;
44
+ /** Un día de ausencia. */
45
+ absence: string;
46
+ /** Cómo se escribe una duración a partir de sus horas y sus minutos. */
47
+ durationValue: (hours: number, minutes: number) => string;
48
+ }
49
+ export interface ClockWidgetProps extends Omit<ComponentPropsWithoutRef<'section'>, 'children' | 'onError'> {
50
+ /** Los tramos del día, en orden. El que no tenga salida es el turno abierto. */
51
+ entries: ClockEntry[];
52
+ /**
53
+ * El día, **ya escrito** («Jueves, 19 de septiembre»). El widget no formatea
54
+ * fechas: eso es `Intl`, y sabe de idiomas y de husos.
55
+ */
56
+ date?: ReactNode;
57
+ /** Estado del día. Default `working`: el único en el que se ficha. */
58
+ dayState?: ClockWidgetDayState;
59
+ /** Se llama al pulsar «fichar entrada». Sin él, el botón no se pinta. */
60
+ onClockIn?: () => void;
61
+ /** Se llama al pulsar «fichar salida». Sin él, el botón no se pinta. */
62
+ onClockOut?: () => void;
63
+ /** La acción está en vuelo: el botón se deshabilita y cambia de texto. */
64
+ pending?: boolean;
65
+ /** Deshabilita el botón sin que la acción esté en vuelo (sin permiso, fuera de horario…). */
66
+ disabled?: boolean;
67
+ /** Lo que falló al fichar. Se pinta como `Alert` de error, que ya se anuncia. */
68
+ error?: ReactNode;
69
+ /**
70
+ * El instante contra el que se mide el turno abierto. **Sin él el reloj
71
+ * corre solo**, un tic por segundo, mientras haya un tramo abierto. Pasarlo
72
+ * lo congela: es lo que usan los tests y las capturas.
73
+ */
74
+ now?: Date;
75
+ /** Locale de la hora de cada tramo. Default `'es-ES'`. */
76
+ locale?: string;
77
+ /** Zona horaria de la hora de cada tramo. Sin ella, la del navegador. */
78
+ timeZone?: string;
79
+ /** Nivel del título del widget. Default 2. */
80
+ headingLevel?: 1 | 2 | 3 | 4 | 5 | 6;
81
+ /** Enseña la tabla de tramos del día. Default `true`. */
82
+ showEntries?: boolean;
83
+ /** El pie del widget: el enlace al histórico de fichajes, por ejemplo. */
84
+ footer?: ReactNode;
85
+ /**
86
+ * Título del widget. Sin él, sale de `clockWidget.title`. Se llama
87
+ * `titleLabel` y no `title` porque `title` ya es un atributo nativo de
88
+ * `<section>` —el bocadillo del navegador— y el widget lo sigue reenviando.
89
+ */
90
+ titleLabel?: string;
91
+ /** Texto del botón de entrada. Sin él, sale de `clockWidget.clockIn`. */
92
+ clockInLabel?: string;
93
+ /** Texto del botón de salida. Sin él, sale de `clockWidget.clockOut`. */
94
+ clockOutLabel?: string;
95
+ /** Texto del botón mientras la acción está en vuelo. Sin él, sale de `clockWidget.pending`. */
96
+ pendingLabel?: string;
97
+ /** Etiqueta de la cifra grande. Sin ella, sale de `clockWidget.elapsed`. */
98
+ elapsedLabel?: string;
99
+ /** Texto del estado del día. Sin él, sale del espacio según `dayState`. */
100
+ dayStateLabel?: ReactNode;
101
+ /** Cómo se escribe una duración. Sin ella, sale de `clockWidget.durationValue`. */
102
+ formatDuration?: (hours: number, minutes: number) => string;
103
+ }
104
+ /**
105
+ * El fichaje del día: en qué estado está, cuánto se lleva trabajado y los
106
+ * tramos ya fichados.
107
+ *
108
+ * **No ficha, ni sabe de permisos, ni de husos.** Recibe los tramos y llama a
109
+ * `onClockIn`/`onClockOut`; quien lo usa decide qué pasa después y le vuelve a
110
+ * pasar los tramos. Tampoco decide si hoy se trabaja: eso llega en `dayState`.
111
+ *
112
+ * El reloj corre solo mientras haya un tramo abierto —un tic por segundo— y se
113
+ * para al cerrarlo. `now` lo congela, que es lo que hacen los tests y las
114
+ * capturas de regresión visual.
115
+ */
116
+ export declare const ClockWidget: import("react").ForwardRefExoticComponent<ClockWidgetProps & import("react").RefAttributes<HTMLElement>>;
@@ -0,0 +1,23 @@
1
+ /**
2
+ * La aritmética del fichaje, aparte del componente para poder probarla sin
3
+ * montar nada: cuántos minutos suma un tramo, cuántos suma el día y si el
4
+ * turno sigue abierto.
5
+ */
6
+ /** Un tramo fichado: la entrada y, si ya se fichó, la salida. */
7
+ export interface ClockEntry {
8
+ id: string;
9
+ /** Cuándo se fichó la entrada. */
10
+ start: Date;
11
+ /** Cuándo se fichó la salida. `null` o ausente mientras el tramo sigue abierto. */
12
+ end?: Date | null;
13
+ }
14
+ /**
15
+ * Los minutos de un tramo. Uno abierto se mide contra `now`; uno que acaba
16
+ * antes de empezar —un reloj que se movió hacia atrás— cuenta cero en vez de
17
+ * restar del total del día.
18
+ */
19
+ export declare function clockEntryMinutes(entry: ClockEntry, now: Date): number;
20
+ /** Los minutos del día: la suma de todos los tramos, con el abierto medido contra `now`. */
21
+ export declare function clockedMinutes(entries: ClockEntry[], now: Date): number;
22
+ /** Si hay algún tramo sin salida: el turno está abierto. */
23
+ export declare function isClockRunning(entries: ClockEntry[]): boolean;