@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.
- package/CHANGELOG.md +118 -0
- package/dist/_shared/tokens.js +160 -0
- package/dist/_types/messages/BrandMessages.d.ts +13 -1
- package/dist/_types/messages/index.d.ts +1 -1
- package/dist/_types/molecules/ClockWidget/ClockWidget.d.ts +116 -0
- package/dist/_types/molecules/ClockWidget/clockDuration.d.ts +23 -0
- package/dist/_types/molecules/Heatmap/Heatmap.d.ts +101 -0
- package/dist/_types/molecules/Heatmap/heatmapScale.d.ts +39 -0
- package/dist/_types/molecules/RecurrenceField/RecurrenceField.d.ts +93 -0
- package/dist/_types/molecules/RecurrenceField/recurrenceRule.d.ts +62 -0
- package/dist/_types/molecules/Timeline/Timeline.d.ts +77 -0
- package/dist/_types/organisms/OrgChart/OrgChart.d.ts +104 -0
- package/dist/_types/organisms/PlanningGrid/PlanningGrid.d.ts +120 -0
- package/dist/_types/organisms/PlanningGrid/planningHours.d.ts +10 -0
- package/dist/brand.css +396 -1
- package/dist/clock-widget.css +1 -0
- package/dist/clock-widget.js +126 -0
- package/dist/heatmap.css +1 -0
- package/dist/heatmap.js +104 -0
- package/dist/org-chart.css +1 -0
- package/dist/org-chart.js +122 -0
- package/dist/planning-grid.css +1 -0
- package/dist/planning-grid.js +167 -0
- package/dist/recurrence-field.css +1 -0
- package/dist/recurrence-field.js +185 -0
- package/dist/timeline.css +1 -0
- package/dist/timeline.js +57 -0
- package/dist/tokens.css +414 -1
- package/package.json +31 -1
- package/src/tokens/index.css +6 -0
- package/src/tokens/molecules/clock-widget.css +36 -0
- package/src/tokens/molecules/clock-widget.ts +7 -0
- package/src/tokens/molecules/heatmap.css +78 -0
- package/src/tokens/molecules/heatmap.ts +7 -0
- package/src/tokens/molecules/org-chart.css +59 -0
- package/src/tokens/molecules/org-chart.ts +7 -0
- package/src/tokens/molecules/planning-grid.css +58 -0
- package/src/tokens/molecules/planning-grid.ts +7 -0
- package/src/tokens/molecules/recurrence-field.css +21 -0
- package/src/tokens/molecules/recurrence-field.ts +7 -0
- package/src/tokens/molecules/timeline.css +36 -0
- package/src/tokens/molecules/timeline.ts +7 -0
- package/src/tokens/scss/_index.legacy.scss +6 -0
- package/src/tokens/scss/_index.scss +6 -0
- package/src/tokens/scss/molecules/_clock-widget.scss +22 -0
- package/src/tokens/scss/molecules/_heatmap.scss +48 -0
- package/src/tokens/scss/molecules/_org-chart.scss +39 -0
- package/src/tokens/scss/molecules/_planning-grid.scss +36 -0
- package/src/tokens/scss/molecules/_recurrence-field.scss +11 -0
- package/src/tokens/scss/molecules/_timeline.scss +22 -0
- package/src/tokens/surface-invert.css +56 -0
- package/src/tokens/surface-light.css +56 -0
- package/src/tokens/surface-public.css +7 -0
- 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
|
package/dist/_shared/tokens.js
CHANGED
|
@@ -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;
|