@studiolxd/brand 49.11.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 (54) hide show
  1. package/CHANGELOG.md +118 -0
  2. package/dist/_shared/tokens.js +160 -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/Timeline/Timeline.d.ts +77 -0
  12. package/dist/_types/organisms/OrgChart/OrgChart.d.ts +104 -0
  13. package/dist/_types/organisms/PlanningGrid/PlanningGrid.d.ts +120 -0
  14. package/dist/_types/organisms/PlanningGrid/planningHours.d.ts +10 -0
  15. package/dist/brand.css +396 -1
  16. package/dist/clock-widget.css +1 -0
  17. package/dist/clock-widget.js +126 -0
  18. package/dist/heatmap.css +1 -0
  19. package/dist/heatmap.js +104 -0
  20. package/dist/org-chart.css +1 -0
  21. package/dist/org-chart.js +122 -0
  22. package/dist/planning-grid.css +1 -0
  23. package/dist/planning-grid.js +167 -0
  24. package/dist/recurrence-field.css +1 -0
  25. package/dist/recurrence-field.js +185 -0
  26. package/dist/timeline.css +1 -0
  27. package/dist/timeline.js +57 -0
  28. package/dist/tokens.css +414 -1
  29. package/package.json +31 -1
  30. package/src/tokens/index.css +6 -0
  31. package/src/tokens/molecules/clock-widget.css +36 -0
  32. package/src/tokens/molecules/clock-widget.ts +7 -0
  33. package/src/tokens/molecules/heatmap.css +78 -0
  34. package/src/tokens/molecules/heatmap.ts +7 -0
  35. package/src/tokens/molecules/org-chart.css +59 -0
  36. package/src/tokens/molecules/org-chart.ts +7 -0
  37. package/src/tokens/molecules/planning-grid.css +58 -0
  38. package/src/tokens/molecules/planning-grid.ts +7 -0
  39. package/src/tokens/molecules/recurrence-field.css +21 -0
  40. package/src/tokens/molecules/recurrence-field.ts +7 -0
  41. package/src/tokens/molecules/timeline.css +36 -0
  42. package/src/tokens/molecules/timeline.ts +7 -0
  43. package/src/tokens/scss/_index.legacy.scss +6 -0
  44. package/src/tokens/scss/_index.scss +6 -0
  45. package/src/tokens/scss/molecules/_clock-widget.scss +22 -0
  46. package/src/tokens/scss/molecules/_heatmap.scss +48 -0
  47. package/src/tokens/scss/molecules/_org-chart.scss +39 -0
  48. package/src/tokens/scss/molecules/_planning-grid.scss +36 -0
  49. package/src/tokens/scss/molecules/_recurrence-field.scss +11 -0
  50. package/src/tokens/scss/molecules/_timeline.scss +22 -0
  51. package/src/tokens/surface-invert.css +56 -0
  52. package/src/tokens/surface-light.css +56 -0
  53. package/src/tokens/surface-public.css +7 -0
  54. package/src/tokens/tokens.json +160 -0
package/CHANGELOG.md CHANGED
@@ -7,6 +7,124 @@ 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
+
10
128
  ## [49.11.0] — 2026-09-19
11
129
 
12
130
  > **Minor.** Tres decisiones de maqueta sobre lo que la 49.10.0 dejó puesto: dónde va el
@@ -2350,6 +2350,25 @@ var e = {
2350
2350
  "--checkbox-field-helper-font-weight": "300",
2351
2351
  "--checkbox-field-helper-line-height": "1.5",
2352
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",
2353
2372
  "--command-palette-max-height": "min(24rem, 60vh)",
2354
2373
  "--command-palette-input-padding-block": "0.75rem",
2355
2374
  "--command-palette-input-padding-inline": "0.5rem",
@@ -2563,6 +2582,51 @@ var e = {
2563
2582
  "--filter-bar-label-offset": "calc(0.875rem * 1.5 + 0.5rem)",
2564
2583
  "--filter-bar-control-block-size": "2.5rem",
2565
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",
2566
2630
  "--image-crop-dialog-gap": "1rem",
2567
2631
  "--image-crop-dialog-area-max-height": "60vh",
2568
2632
  "--image-crop-dialog-area-min-height": "60vh",
@@ -2824,6 +2888,14 @@ var e = {
2824
2888
  "--recovery-codes-line-height": "1.65",
2825
2889
  "--recovery-codes-color": "#111e30",
2826
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",
2827
2899
  "--search-form-inline-size": "100%",
2828
2900
  "--search-form-xl-height": "4rem",
2829
2901
  "--search-form-xl-padding-inline": "2rem",
@@ -3030,6 +3102,25 @@ var e = {
3030
3102
  "--time-field-helper-font-weight": "300",
3031
3103
  "--time-field-helper-line-height": "1.5",
3032
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",
3033
3124
  "--tree-view-icon-size": "16px",
3034
3125
  "--tree-view-indent": "calc(16px + 0.5rem)",
3035
3126
  "--tree-view-row-gap": "0.5rem",
@@ -3242,6 +3333,75 @@ var e = {
3242
3333
  "--onboarding-shell-actions-gap": "1rem",
3243
3334
  "--onboarding-shell-actions-margin-block-start": "2rem",
3244
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",
3245
3405
  "--public-page-shell-preferences-padding-block": "1.5rem",
3246
3406
  "--public-page-shell-preferences-gap": "0.75rem",
3247
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;
@@ -0,0 +1,101 @@
1
+ import { type ComponentPropsWithoutRef, type ReactNode } from 'react';
2
+ import './Heatmap.css';
3
+ export type { HeatmapScale } from './heatmapScale';
4
+ /**
5
+ * Los tres textos de la matriz, y los tres son **cromo**: cómo se llama la
6
+ * tabla, cómo se dice que una casilla no tiene dato y cómo se llama la
7
+ * leyenda. Los nombres de filas y columnas son contenido, y los escribe quien
8
+ * pasa los datos.
9
+ */
10
+ export interface HeatmapMessages {
11
+ /** Nombre accesible de la matriz cuando la pantalla no le da uno propio. */
12
+ label: string;
13
+ /** Cómo se dice que una casilla no tiene dato — nadie la midió. */
14
+ empty: string;
15
+ /** Nombre accesible de la leyenda de la rampa. */
16
+ scale: string;
17
+ }
18
+ /** Una fila: una persona, un puesto. */
19
+ export interface HeatmapRow {
20
+ id: string;
21
+ /** Cómo se llama la fila. Admite un nodo: un enlace a la ficha, por ejemplo. */
22
+ label: ReactNode;
23
+ }
24
+ /** Una columna: una competencia, un mes. */
25
+ export interface HeatmapColumn {
26
+ key: string;
27
+ /** Cómo se llama la columna. */
28
+ label: ReactNode;
29
+ /**
30
+ * Grupo al que pertenece (la categoría de la competencia). Las columnas
31
+ * consecutivas con el mismo grupo se agrupan bajo una cabecera común; el
32
+ * orden de las columnas lo decide quien las pasa, no la matriz.
33
+ */
34
+ group?: string;
35
+ }
36
+ /** Una casilla. Lo que no venga en la lista es una casilla **sin dato**. */
37
+ export interface HeatmapCell {
38
+ rowId: string;
39
+ columnKey: string;
40
+ /** La magnitud. `null` es sin dato, que no es lo mismo que el mínimo. */
41
+ value: number | null;
42
+ }
43
+ export interface HeatmapProps extends Omit<ComponentPropsWithoutRef<'div'>, 'children'> {
44
+ rows: HeatmapRow[];
45
+ columns: HeatmapColumn[];
46
+ /** Las casillas con dato. Las que falten se pintan sin dato. */
47
+ cells: HeatmapCell[];
48
+ /** Extremo bajo del dominio. Default 0. */
49
+ min?: number;
50
+ /**
51
+ * Extremo alto del dominio. **Sin él sale del mayor valor de `cells`**, que
52
+ * sirve para explorar pero hace que dos matrices del mismo panel no se
53
+ * puedan comparar: en cuanto haya dos, se pasa.
54
+ */
55
+ max?: number;
56
+ /** Pasos de la rampa, de 2 a 6. Default 5. */
57
+ steps?: number;
58
+ /** Encabezado de la columna de filas («Persona», «Puesto»). */
59
+ rowHeader?: ReactNode;
60
+ /** Pinta la cifra dentro de la celda. Default `true`. Con `false` sigue leyéndose. */
61
+ showValues?: boolean;
62
+ /** Cómo se escribe una magnitud. Default: la cifra tal cual, con `Intl`. */
63
+ formatValue?: (value: number) => string;
64
+ /** Locale del formato por defecto. Default `'es-ES'`. */
65
+ locale?: string;
66
+ /** Enseña la leyenda de la rampa. Default `true`. */
67
+ showLegend?: boolean;
68
+ /** Rótulo del extremo bajo de la leyenda. Default: el mínimo formateado. */
69
+ minLabel?: ReactNode;
70
+ /** Rótulo del extremo alto de la leyenda. Default: el máximo formateado. */
71
+ maxLabel?: ReactNode;
72
+ /**
73
+ * Nombre accesible de la matriz. **Sin él**, sale de `heatmap.label` del
74
+ * `BrandMessagesProvider`.
75
+ */
76
+ label?: string;
77
+ /**
78
+ * Cómo se dice que una casilla no tiene dato. **Sin él**, sale de
79
+ * `heatmap.empty`.
80
+ */
81
+ emptyLabel?: string;
82
+ /**
83
+ * Nombre accesible de la leyenda. **Sin él**, sale de `heatmap.scale`. Solo
84
+ * se lee con `showLegend`.
85
+ */
86
+ scaleLabel?: string;
87
+ }
88
+ /**
89
+ * La matriz de valores con escala de color: competencias por persona,
90
+ * cobertura por puesto.
91
+ *
92
+ * **La cifra va dentro de la celda**, así que el color no es nunca la única
93
+ * señal: quien no distingue un azul de otro lee el número, y quien usa un
94
+ * lector de pantalla oye el nombre de la fila, el de la columna y el valor,
95
+ * porque la tabla tiene encabezados de verdad en los dos ejes.
96
+ *
97
+ * No calcula nada: recibe las casillas y el dominio (`min`/`max`) y reparte.
98
+ * Lo que no venga en `cells` es una casilla **sin dato**, que se pinta rayada
99
+ * y no como el mínimo.
100
+ */
101
+ export declare const Heatmap: import("react").ForwardRefExoticComponent<HeatmapProps & import("react").RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,39 @@
1
+ /**
2
+ * El reparto de una magnitud en pasos de color, aparte del componente para
3
+ * poder probarlo sin montar nada.
4
+ */
5
+ /**
6
+ * Los pasos que tiene la rampa de la matriz: **seis**, no los siete de la
7
+ * rampa secuencial del sistema.
8
+ *
9
+ * El paso `sequential-400` se queda fuera a propósito. La cifra se pinta
10
+ * DENTRO de la celda, así que cada relleno tiene que admitir una de las dos
11
+ * tintas del sistema a 4,5:1, y el 400 sobre superficie oscura
12
+ * (`sequential-400-on-dark`, #2978DE) no llega con ninguna: 4,34:1 con la
13
+ * tinta clara y 3,86:1 con la prusia. Los otros seis pasos sí llegan en las
14
+ * dos superficies. Inventar un color para tapar ese hueco sería ampliar la
15
+ * paleta por un rol, que es justo lo que el sistema no hace.
16
+ */
17
+ export declare const HEATMAP_RAMP_STEPS = 6;
18
+ /** El dominio de la matriz: de dónde a dónde va la magnitud, y en cuántos pasos. */
19
+ export interface HeatmapScale {
20
+ min: number;
21
+ max: number;
22
+ /** Pasos visibles, de 2 a `HEATMAP_RAMP_STEPS`. */
23
+ steps: number;
24
+ }
25
+ /**
26
+ * El paso que le toca a un valor, de 1 a `scale.steps`. `null` es **sin dato**
27
+ * —nadie midió esa casilla—, que no es lo mismo que el mínimo.
28
+ *
29
+ * El mínimo cae en el paso 1 y el máximo en el último; lo que se sale del
30
+ * dominio se recorta contra él en vez de desbordar la rampa.
31
+ */
32
+ export declare function heatmapStep(value: number | null | undefined, scale: HeatmapScale): number | null;
33
+ /**
34
+ * El peldaño de la rampa del sistema (1…6) que pinta el paso `step` de una
35
+ * escala de `steps` pasos. Con menos pasos que peldaños, la escala se reparte
36
+ * por la rampa entera: el primero siempre es el 1 y el último siempre es el 6,
37
+ * para que dos matrices con distinto número de pasos se lean igual de lejos.
38
+ */
39
+ export declare function heatmapRampIndex(step: number, steps: number): number;