@studiolxd/brand 45.0.0 → 46.0.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 +70 -0
- package/dist/_shared/tokens.js +9 -0
- package/dist/_types/messages/BrandMessages.d.ts +13 -1
- package/dist/_types/messages/index.d.ts +1 -1
- package/dist/_types/molecules/AppLauncher/AppLauncher.d.ts +43 -8
- package/dist/_types/molecules/CommandPalette/CommandPalette.d.ts +43 -8
- package/dist/_types/molecules/Consent/Consent.d.ts +93 -20
- package/dist/_types/molecules/NotificationButton/NotificationButton.d.ts +17 -4
- package/dist/_types/molecules/NotificationPanel/NotificationPanel.d.ts +54 -8
- package/dist/_types/sections/FloatingDock/FloatingDock.d.ts +33 -3
- package/dist/app-launcher.js +67 -61
- package/dist/brand.css +137 -6
- package/dist/command-palette.js +38 -36
- package/dist/consent.js +75 -73
- package/dist/floating-dock.js +42 -41
- package/dist/multi-select.css +1 -1
- package/dist/multi-select.js +1 -2
- package/dist/notification-button.js +18 -17
- package/dist/notification-panel.js +81 -80
- package/dist/select.css +1 -1
- package/dist/select.js +1 -2
- package/dist/tokens.css +137 -6
- package/package.json +1 -1
- package/src/tokens/components/control.css +4 -1
- package/src/tokens/components/multi-select.css +3 -0
- package/src/tokens/components/select.css +3 -0
- package/src/tokens/molecules/filter-bar.css +2 -2
- package/src/tokens/scss/components/_control.scss +4 -1
- package/src/tokens/scss/components/_multi-select.scss +3 -0
- package/src/tokens/scss/components/_select.scss +3 -0
- package/src/tokens/scss/molecules/_filter-bar.scss +2 -2
- package/src/tokens/surface-public.css +124 -2
- package/src/tokens/tokens.json +9 -0
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,76 @@ 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
|
+
## [46.0.0] — 2026-09-15
|
|
11
|
+
|
|
12
|
+
> **Major.** Séptima familia al proveedor de textos: los envoltorios de diálogo. `Consent`,
|
|
13
|
+
> `CommandPalette`, `AppLauncher`, `FloatingDock`, `NotificationButton` y
|
|
14
|
+
> `NotificationPanel` dejan de traer castellano puesto.
|
|
15
|
+
|
|
16
|
+
### Los envoltorios leen del proveedor
|
|
17
|
+
|
|
18
|
+
Seis espacios nuevos en `BrandMessages`: `consent`, `commandPalette`, `appLauncher`,
|
|
19
|
+
`floatingDock`, `notificationButton` y `notificationPanel`. Mismo orden —**prop →
|
|
20
|
+
proveedor → error**—. `CommandPalette` y `AppLauncher` relajan su API de paso: los textos
|
|
21
|
+
que antes eran props obligatorias en cada uso ahora son opcionales, porque los pone el
|
|
22
|
+
catálogo.
|
|
23
|
+
|
|
24
|
+
### Lo que se queda como prop, por valor y no por nombre
|
|
25
|
+
|
|
26
|
+
`ConsentBanner.description` y `policyLabel` son **obligatorias y sin default**, como el
|
|
27
|
+
`confirmLabel` de un diálogo: el texto legal depende de la jurisdicción y de lo que cada
|
|
28
|
+
producto guarde. El tipo exige `policyLabel` en cuanto hay `policyHref` —o el enlace no
|
|
29
|
+
está, o está entero—, así que una política sin nombre no compila.
|
|
30
|
+
|
|
31
|
+
`AppLauncherLabels.trigger` no va al catálogo por una razón distinta: su **presencia**
|
|
32
|
+
decide la cara del disparador (solo icono, o icono con rótulo), y un texto de catálogo está
|
|
33
|
+
siempre presente. Meterlo ahí obligaría a todos los lanzadores de la suite a llevar rótulo.
|
|
34
|
+
|
|
35
|
+
### `FloatingDock` tiene su propio aspa, y por qué no pasa por `Modal`
|
|
36
|
+
|
|
37
|
+
Se miró antes de decidirlo, y la razón es real: es un diálogo **no modal** —sin velo, con
|
|
38
|
+
la página viva detrás—, no atrapa el foco, su portal monta dentro del propio ancla para
|
|
39
|
+
heredar la superficie por cascada, y va anclado a una esquina con el aspa fuera de la
|
|
40
|
+
cabecera. El `Modal` del sistema es lo contrario en las cuatro cosas. Hacerlo pasar por él
|
|
41
|
+
no era quitar una clave: era cambiar el componente. Queda escrito en el componente y en su
|
|
42
|
+
documentación para que no se vuelva a abrir.
|
|
43
|
+
|
|
44
|
+
## [45.1.0] — 2026-09-15
|
|
45
|
+
|
|
46
|
+
> **Minor.** La superficie pública pone los controles en talla lg, igual que hace con el
|
|
47
|
+
> texto.
|
|
48
|
+
|
|
49
|
+
### `.site-shell` parte de lg también en los controles
|
|
50
|
+
|
|
51
|
+
El bloque subía el cuerpo a 20px y dejaba los campos en la talla de aplicación: en una
|
|
52
|
+
página pública el texto era grande y los buscadores, filtros y botones de al lado no. Misma
|
|
53
|
+
omisión que tenía el término de la ficha hasta la v39.0.0, y misma respuesta: en el bloque,
|
|
54
|
+
no en cada app.
|
|
55
|
+
|
|
56
|
+
No se enumeran controles a mano. Cada control ya declara su talla lg en tokens hermanos
|
|
57
|
+
(`input.lg-height` junto a `input.height`), de donde bebe su modificador `--lg`; ahora
|
|
58
|
+
`.site-shell` apunta cada token de partida a su hermano `lg-*`, y lo que hereda de un
|
|
59
|
+
control sube con él. Un control nuevo entra solo por tener sus tokens `lg-*`. La familia
|
|
60
|
+
base (`control.*`) estrena su par lg, y el motor de tokens arrastra ahora una referencia
|
|
61
|
+
metida dentro de una fórmula —sin eso el renglón reservado para el rótulo de la barra de
|
|
62
|
+
filtros se quedaba corto y descolocaba el interruptor—.
|
|
63
|
+
|
|
64
|
+
**El botón entra**: 40→48px y 16→20px, porque un botón md junto a un campo de 48 se lee
|
|
65
|
+
como un control de otra talla justo donde más se juntan. **El chevron del `Select`** era lo
|
|
66
|
+
único de la caja que no subía —su tamaño salía de una prop de React y no de un token— y
|
|
67
|
+
ahora es token (16→24px).
|
|
68
|
+
|
|
69
|
+
Medido en Chromium: dentro del shell, campo, selector, selector múltiple, campo de fecha y
|
|
70
|
+
botón a 48px/20px, exactamente lg; fuera, 40px/16px. El interruptor, su campo y el botón de
|
|
71
|
+
acciones de la barra siguen con el centro en el mismo píxel.
|
|
72
|
+
|
|
73
|
+
**Dos matices, documentados.** `size="sm"` sigue mandando dentro del shell; lo que ya no
|
|
74
|
+
se distingue de lg es un `size="md"` escrito a mano. Y **lo que sale por un portal** —la
|
|
75
|
+
lista de un `Select`, el calendario de un `DatePicker`— monta en `document.body` y no
|
|
76
|
+
hereda la superficie: el disparador va a 48/20 y su lista se queda en la talla de
|
|
77
|
+
aplicación. Es el mismo caso que `Modal` y `Sheet` documentan, y la misma respuesta
|
|
78
|
+
(`container`), pero los campos `*Field` no lo exponen todavía.
|
|
79
|
+
|
|
10
80
|
## [45.0.0] — 2026-09-15
|
|
11
81
|
|
|
12
82
|
> **Major.** Sexta familia al proveedor de textos: diálogos y superficies. `Modal`, `Sheet`,
|
package/dist/_shared/tokens.js
CHANGED
|
@@ -684,6 +684,9 @@ var e = {
|
|
|
684
684
|
"--control-focus-ring-inset-offset": "1px",
|
|
685
685
|
"--control-padding-inline": "1rem",
|
|
686
686
|
"--control-padding-block": "1rem",
|
|
687
|
+
"--control-lg-height": "3rem",
|
|
688
|
+
"--control-lg-font-size": "1.25rem",
|
|
689
|
+
"--control-lg-padding-inline": "1.5rem",
|
|
687
690
|
"--copyable-value-gap": "0.75rem",
|
|
688
691
|
"--description-list-border-width": "1px",
|
|
689
692
|
"--description-list-border-style": "solid",
|
|
@@ -1202,6 +1205,9 @@ var e = {
|
|
|
1202
1205
|
"--multi-select-item-selected-font-weight": "700",
|
|
1203
1206
|
"--multi-select-icon-transition-easing": "ease",
|
|
1204
1207
|
"--multi-select-selection-gap": "0.25rem",
|
|
1208
|
+
"--multi-select-icon-size": "16px",
|
|
1209
|
+
"--multi-select-sm-icon-size": "8px",
|
|
1210
|
+
"--multi-select-lg-icon-size": "24px",
|
|
1205
1211
|
"--multi-select-sm-font-size": "0.875rem",
|
|
1206
1212
|
"--multi-select-sm-padding-inline": "0.5rem",
|
|
1207
1213
|
"--multi-select-sm-item-padding-block": "0.25rem",
|
|
@@ -1475,6 +1481,9 @@ var e = {
|
|
|
1475
1481
|
"--select-item-selected-font-weight": "500",
|
|
1476
1482
|
"--select-item-highlighted-bg": "#111e30",
|
|
1477
1483
|
"--select-item-highlighted-color": "#ffffff",
|
|
1484
|
+
"--select-icon-size": "16px",
|
|
1485
|
+
"--select-sm-icon-size": "8px",
|
|
1486
|
+
"--select-lg-icon-size": "24px",
|
|
1478
1487
|
"--select-sm-font-size": "0.875rem",
|
|
1479
1488
|
"--select-sm-padding-inline": "0.5rem",
|
|
1480
1489
|
"--select-sm-item-padding-block": "0.25rem",
|
|
@@ -25,6 +25,12 @@ import type { ConfirmDialogMessages } from '../molecules/ConfirmDialog/ConfirmDi
|
|
|
25
25
|
import type { AlertMessages } from '../molecules/Alert/Alert';
|
|
26
26
|
import type { BannerMessages } from '../molecules/Banner/Banner';
|
|
27
27
|
import type { ToasterMessages } from '../molecules/Toast/Toaster';
|
|
28
|
+
import type { ConsentMessages } from '../molecules/Consent/Consent';
|
|
29
|
+
import type { CommandPaletteMessages } from '../molecules/CommandPalette/CommandPalette';
|
|
30
|
+
import type { AppLauncherMessages } from '../molecules/AppLauncher/AppLauncher';
|
|
31
|
+
import type { FloatingDockMessages } from '../sections/FloatingDock/FloatingDock';
|
|
32
|
+
import type { NotificationButtonMessages } from '../molecules/NotificationButton/NotificationButton';
|
|
33
|
+
import type { NotificationPanelMessages } from '../molecules/NotificationPanel/NotificationPanel';
|
|
28
34
|
/**
|
|
29
35
|
* El contrato de textos de la librería: un espacio por componente, y dentro
|
|
30
36
|
* de cada espacio **todas las claves obligatorias**.
|
|
@@ -74,5 +80,11 @@ export interface BrandMessages {
|
|
|
74
80
|
alert: AlertMessages;
|
|
75
81
|
banner: BannerMessages;
|
|
76
82
|
toaster: ToasterMessages;
|
|
83
|
+
consent: ConsentMessages;
|
|
84
|
+
commandPalette: CommandPaletteMessages;
|
|
85
|
+
appLauncher: AppLauncherMessages;
|
|
86
|
+
floatingDock: FloatingDockMessages;
|
|
87
|
+
notificationButton: NotificationButtonMessages;
|
|
88
|
+
notificationPanel: NotificationPanelMessages;
|
|
77
89
|
}
|
|
78
|
-
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, };
|
|
90
|
+
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, };
|
|
@@ -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, } 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, } from './BrandMessages';
|
|
@@ -1,4 +1,24 @@
|
|
|
1
1
|
import './AppLauncher.css';
|
|
2
|
+
/**
|
|
3
|
+
* El cromo del lanzador, y **solo el cromo**: el nombre accesible del
|
|
4
|
+
* disparador, el título del diálogo y la marca de app nueva. Las tres valen
|
|
5
|
+
* igual en todas las apps de la suite —el lanzador es el mismo mueble en
|
|
6
|
+
* todas— y ninguna nombra una aplicación concreta: eso viaja en `apps`.
|
|
7
|
+
*
|
|
8
|
+
* **`trigger` no está aquí** y sigue siendo prop. No es un texto de más: su
|
|
9
|
+
* presencia decide la cara del disparador —solo icono, o icono con rótulo—, y
|
|
10
|
+
* un texto de catálogo está siempre presente, así que ponerlo aquí obligaría a
|
|
11
|
+
* todos los lanzadores de la suite a llevar rótulo. Es una decisión de
|
|
12
|
+
* maqueta que se toma en el sitio donde se monta.
|
|
13
|
+
*/
|
|
14
|
+
export interface AppLauncherMessages {
|
|
15
|
+
/** Nombre accesible del disparador cuando es solo icono («Abrir launcher de apps»). */
|
|
16
|
+
open: string;
|
|
17
|
+
/** Texto del distintivo de app nueva. */
|
|
18
|
+
new: string;
|
|
19
|
+
/** Título del diálogo con `presentation="modal"`. */
|
|
20
|
+
title: string;
|
|
21
|
+
}
|
|
2
22
|
export interface LauncherApp {
|
|
3
23
|
id: string;
|
|
4
24
|
name: string;
|
|
@@ -6,10 +26,18 @@ export interface LauncherApp {
|
|
|
6
26
|
isNew?: boolean;
|
|
7
27
|
}
|
|
8
28
|
export interface AppLauncherLabels {
|
|
9
|
-
/**
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
29
|
+
/**
|
|
30
|
+
* Texto accesible del trigger («Abrir launcher de apps»). Solo se usa como
|
|
31
|
+
* `aria-label` cuando no hay `trigger`: con texto visible, el nombre
|
|
32
|
+
* accesible es ese texto. **Sin default**: sin él, sale de
|
|
33
|
+
* `appLauncher.open` del `BrandMessagesProvider`.
|
|
34
|
+
*/
|
|
35
|
+
open?: string;
|
|
36
|
+
/**
|
|
37
|
+
* Texto del badge de app nueva. **Sin default**: sin él, sale de
|
|
38
|
+
* `appLauncher.new` del proveedor.
|
|
39
|
+
*/
|
|
40
|
+
new?: string;
|
|
13
41
|
/**
|
|
14
42
|
* Texto visible del disparador (p. ej. «Aplicaciones»), a la derecha del
|
|
15
43
|
* icono de rejilla. Sin él, el disparador se queda como hoy: solo icono,
|
|
@@ -17,14 +45,21 @@ export interface AppLauncherLabels {
|
|
|
17
45
|
*/
|
|
18
46
|
trigger?: string;
|
|
19
47
|
/**
|
|
20
|
-
* Título del diálogo cuando `presentation="modal"`.
|
|
21
|
-
*
|
|
48
|
+
* Título del diálogo cuando `presentation="modal"`. **Sin default**: sin él,
|
|
49
|
+
* sale de `appLauncher.title` del proveedor. Sin uso en
|
|
50
|
+
* `presentation="popover"`, que no lleva título.
|
|
22
51
|
*/
|
|
23
52
|
title?: string;
|
|
24
53
|
}
|
|
25
54
|
export interface AppLauncherProps {
|
|
26
55
|
apps: LauncherApp[];
|
|
27
|
-
|
|
56
|
+
/**
|
|
57
|
+
* Anulaciones puntuales de los textos del lanzador. **Ya no es
|
|
58
|
+
* obligatoria**: sin ella, el cromo sale del espacio `appLauncher` del
|
|
59
|
+
* `BrandMessagesProvider`. Sigue haciendo falta para `trigger`, que no es
|
|
60
|
+
* catálogo.
|
|
61
|
+
*/
|
|
62
|
+
labels?: AppLauncherLabels;
|
|
28
63
|
/** Id de la app actual — se marca en la rejilla. */
|
|
29
64
|
currentAppId?: string;
|
|
30
65
|
open?: boolean;
|
|
@@ -39,4 +74,4 @@ export interface AppLauncherProps {
|
|
|
39
74
|
*/
|
|
40
75
|
presentation?: 'modal' | 'popover';
|
|
41
76
|
}
|
|
42
|
-
export declare function AppLauncher({ presentation, ...rest }: AppLauncherProps): import("react/jsx-runtime").JSX.Element;
|
|
77
|
+
export declare function AppLauncher({ presentation, labels, ...rest }: AppLauncherProps): import("react/jsx-runtime").JSX.Element;
|
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
import { type ReactNode } from 'react';
|
|
2
2
|
import './CommandPalette.css';
|
|
3
|
+
/**
|
|
4
|
+
* El cromo de la paleta, y **solo el cromo**: las cuatro cosas que la paleta
|
|
5
|
+
* dice por su cuenta, sin que se las pase nadie.
|
|
6
|
+
*
|
|
7
|
+
* Ninguna nombra un comando: la paleta se llama igual en todas las pantallas
|
|
8
|
+
* («Buscar un comando»), el buscador pide lo mismo, el vacío dice que no hay
|
|
9
|
+
* nada y la lista solo necesita un nombre para el lector. Lo que sí cambia
|
|
10
|
+
* —los grupos y sus ítems— viaja en `groups` y lo escribe la aplicación.
|
|
11
|
+
*
|
|
12
|
+
* El aspa **no está aquí**: es un reenvío puro al `Modal`, y sin `closeLabel`
|
|
13
|
+
* lee `modal.close` como cualquier otro diálogo.
|
|
14
|
+
*/
|
|
15
|
+
export interface CommandPaletteMessages {
|
|
16
|
+
/** Título accesible y visible del diálogo. */
|
|
17
|
+
title: string;
|
|
18
|
+
/** Marcador del buscador. */
|
|
19
|
+
placeholder: string;
|
|
20
|
+
/** Texto del estado «sin resultados». */
|
|
21
|
+
empty: string;
|
|
22
|
+
/** Nombre accesible de la lista de resultados (`role="listbox"`). */
|
|
23
|
+
list: string;
|
|
24
|
+
}
|
|
3
25
|
export interface CommandPaletteItem {
|
|
4
26
|
/** Clave estable del ítem. */
|
|
5
27
|
id: string;
|
|
@@ -24,17 +46,30 @@ export interface CommandPaletteProps {
|
|
|
24
46
|
* filtrar no se renderizan — el call-site no necesita condicionarlos.
|
|
25
47
|
*/
|
|
26
48
|
groups: CommandPaletteGroup[];
|
|
27
|
-
/** Título accesible y visible del diálogo. */
|
|
28
|
-
title: string;
|
|
29
|
-
placeholder: string;
|
|
30
|
-
/** Texto del estado "sin resultados". */
|
|
31
|
-
emptyLabel: string;
|
|
32
49
|
/**
|
|
33
|
-
*
|
|
34
|
-
*
|
|
50
|
+
* Título accesible y visible del diálogo. **Sin default**: sin él, sale de
|
|
51
|
+
* `commandPalette.title` del `BrandMessagesProvider`.
|
|
52
|
+
*/
|
|
53
|
+
title?: string;
|
|
54
|
+
/**
|
|
55
|
+
* Marcador del buscador. **Sin default**: sin él, sale de
|
|
56
|
+
* `commandPalette.placeholder` del proveedor.
|
|
57
|
+
*/
|
|
58
|
+
placeholder?: string;
|
|
59
|
+
/**
|
|
60
|
+
* Texto del estado «sin resultados». **Sin default**: sin él, sale de
|
|
61
|
+
* `commandPalette.empty` del proveedor.
|
|
62
|
+
*/
|
|
63
|
+
emptyLabel?: string;
|
|
64
|
+
/**
|
|
65
|
+
* Nombre accesible de la lista de resultados (`role="listbox"`). **Sin
|
|
66
|
+
* default**: sin ella, sale de `commandPalette.list` del proveedor.
|
|
35
67
|
*/
|
|
36
68
|
listLabel?: string;
|
|
37
|
-
/**
|
|
69
|
+
/**
|
|
70
|
+
* Nombre accesible del aspa del diálogo. **Reenvío puro** al `Modal`: sin
|
|
71
|
+
* él, el aspa lee `modal.close` del proveedor.
|
|
72
|
+
*/
|
|
38
73
|
closeLabel?: string;
|
|
39
74
|
/**
|
|
40
75
|
* Tecla del atajo global (con ⌘ o Ctrl) que abre y cierra la paleta.
|
|
@@ -1,6 +1,43 @@
|
|
|
1
1
|
import { type ReactNode } from 'react';
|
|
2
2
|
import './Consent.css';
|
|
3
3
|
import { Modal } from '../Modal/Modal';
|
|
4
|
+
/**
|
|
5
|
+
* El cromo del consentimiento, y **solo el cromo**: lo que la banda y el
|
|
6
|
+
* panel dicen por su cuenta y vale igual en cualquier sitio que los monte.
|
|
7
|
+
*
|
|
8
|
+
* La línea cae aquí en medio de una pieza legal, así que conviene decirla
|
|
9
|
+
* entera:
|
|
10
|
+
*
|
|
11
|
+
* - **Los botones son cromo.** «Aceptar todas», «Rechazar» y «Preferencias»
|
|
12
|
+
* nombran las tres salidas que la ePrivacy pide, y son las mismas en todos
|
|
13
|
+
* los sitios: no describen qué se guarda, describen qué hace el botón.
|
|
14
|
+
* - **El título y el nombre de la región son cromo.** «Cookies» nombra el
|
|
15
|
+
* mecanismo, no la política de nadie.
|
|
16
|
+
* - **`description` y `policyLabel` NO están aquí y son obligatorias.** El
|
|
17
|
+
* texto legal depende de la jurisdicción y de lo que cada producto guarde
|
|
18
|
+
* de verdad: un default de catálogo lo haría decir lo mismo en todas partes
|
|
19
|
+
* —que es exactamente lo que un texto legal no puede hacer— y, siendo
|
|
20
|
+
* opcional, nadie llegaría a escribir el bueno. Como el `confirmLabel` del
|
|
21
|
+
* `ConfirmDialog`.
|
|
22
|
+
* - **Las categorías tampoco.** Su nombre y su descripción son contenido: qué
|
|
23
|
+
* guarda cada una lo sabe el producto.
|
|
24
|
+
*/
|
|
25
|
+
export interface ConsentMessages {
|
|
26
|
+
/** Título de la banda. */
|
|
27
|
+
title: string;
|
|
28
|
+
/** Nombre accesible de la región de la banda (`role="region"`). */
|
|
29
|
+
regionLabel: string;
|
|
30
|
+
/** Rótulo del botón que acepta todas las categorías. */
|
|
31
|
+
acceptAll: string;
|
|
32
|
+
/** Rótulo del botón que rechaza las opcionales. */
|
|
33
|
+
rejectAll: string;
|
|
34
|
+
/** Rótulo del botón que abre el panel de preferencias. */
|
|
35
|
+
preferences: string;
|
|
36
|
+
/** Título del panel de preferencias. */
|
|
37
|
+
preferencesTitle: string;
|
|
38
|
+
/** Marca, solo para lectores de pantalla, de una categoría necesaria. */
|
|
39
|
+
alwaysOn: string;
|
|
40
|
+
}
|
|
4
41
|
/** Una categoría de tecnologías sobre la que se pide (o no) consentimiento. */
|
|
5
42
|
export interface ConsentCategory {
|
|
6
43
|
/** Identificador de la categoría. Es la clave dentro de `value`. */
|
|
@@ -18,7 +55,25 @@ export interface ConsentCategory {
|
|
|
18
55
|
}
|
|
19
56
|
/** La decisión: qué categorías están aceptadas, por `id`. */
|
|
20
57
|
export type ConsentValue = Record<string, boolean>;
|
|
21
|
-
|
|
58
|
+
/**
|
|
59
|
+
* El enlace a la política: o no está, o está **entero**. El texto del enlace
|
|
60
|
+
* es contenido —lo escribe la app y depende de la jurisdicción—, así que no
|
|
61
|
+
* sale del catálogo, y una `policyHref` sin `policyLabel` dejaría un enlace
|
|
62
|
+
* sin nombre. El tipo lo impide en compilación.
|
|
63
|
+
*/
|
|
64
|
+
export type ConsentPolicyProps = {
|
|
65
|
+
policyHref?: undefined;
|
|
66
|
+
policyLabel?: undefined;
|
|
67
|
+
policyExternal?: undefined;
|
|
68
|
+
} | {
|
|
69
|
+
/** URL de la política de cookies. */
|
|
70
|
+
policyHref: string;
|
|
71
|
+
/** Texto del enlace a la política. **Obligatorio con `policyHref`**: es contenido, no sale del catálogo. */
|
|
72
|
+
policyLabel: string;
|
|
73
|
+
/** Abre la política en otra pestaña (la política suele vivir en la web pública). */
|
|
74
|
+
policyExternal?: boolean;
|
|
75
|
+
};
|
|
76
|
+
export interface ConsentBannerBaseProps extends Omit<React.ComponentPropsWithoutRef<'aside'>, 'title' | 'children' | 'onChange'> {
|
|
22
77
|
/**
|
|
23
78
|
* Muestra la banda. Es la primera visita, o la decisión ha caducado: quién lo
|
|
24
79
|
* sabe es el consumidor, que es también quien guarda la cookie. Default: `true`.
|
|
@@ -30,28 +85,41 @@ export interface ConsentBannerProps extends Omit<React.ComponentPropsWithoutRef<
|
|
|
30
85
|
onRejectAll: () => void;
|
|
31
86
|
/** Abre el panel de preferencias. Sin ella no se pinta el botón. */
|
|
32
87
|
onOpenPreferences?: () => void;
|
|
33
|
-
/**
|
|
88
|
+
/**
|
|
89
|
+
* Título de la banda. **Sin default**: sin él, sale de `consent.title` del
|
|
90
|
+
* `BrandMessagesProvider`.
|
|
91
|
+
*/
|
|
34
92
|
title?: ReactNode;
|
|
35
|
-
/**
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
|
|
43
|
-
|
|
93
|
+
/**
|
|
94
|
+
* Texto de la banda: qué se guarda y por qué. **Obligatorio y sin default**
|
|
95
|
+
* — es el texto legal, depende de la jurisdicción y de lo que el producto
|
|
96
|
+
* guarde de verdad, así que no sale del catálogo. Ver `ConsentMessages`.
|
|
97
|
+
*/
|
|
98
|
+
description: ReactNode;
|
|
99
|
+
/**
|
|
100
|
+
* Etiqueta del botón de aceptar. **Sin default**: sin ella, sale de
|
|
101
|
+
* `consent.acceptAll` del proveedor.
|
|
102
|
+
*/
|
|
44
103
|
acceptAllLabel?: string;
|
|
45
|
-
/**
|
|
104
|
+
/**
|
|
105
|
+
* Etiqueta del botón de rechazar. **Sin default**: sin ella, sale de
|
|
106
|
+
* `consent.rejectAll` del proveedor.
|
|
107
|
+
*/
|
|
46
108
|
rejectAllLabel?: string;
|
|
47
|
-
/**
|
|
109
|
+
/**
|
|
110
|
+
* Etiqueta del botón de preferencias. **Sin default**: sin ella, sale de
|
|
111
|
+
* `consent.preferences` del proveedor. Solo se lee si hay
|
|
112
|
+
* `onOpenPreferences`: sin botón no se exige su texto.
|
|
113
|
+
*/
|
|
48
114
|
preferencesLabel?: string;
|
|
49
115
|
/**
|
|
50
|
-
* Nombre accesible de la región.
|
|
51
|
-
*
|
|
116
|
+
* Nombre accesible de la región. **Sin default**: sin él, sale de
|
|
117
|
+
* `consent.regionLabel` del proveedor. Pásalo cuando el título lleve JSX y
|
|
118
|
+
* quieras que la región se nombre con otra cosa.
|
|
52
119
|
*/
|
|
53
120
|
regionLabel?: string;
|
|
54
121
|
}
|
|
122
|
+
export type ConsentBannerProps = ConsentBannerBaseProps & ConsentPolicyProps;
|
|
55
123
|
/**
|
|
56
124
|
* La banda de consentimiento: qué se quiere guardar y tres salidas —aceptar,
|
|
57
125
|
* rechazar, decidir por categorías—. **No es un diálogo**: no atrapa el foco, no
|
|
@@ -91,17 +159,22 @@ export interface ConsentPreferencesProps {
|
|
|
91
159
|
surface?: 'sheet' | 'modal';
|
|
92
160
|
/** Borde por el que entra el panel (solo con `surface="sheet"`, la alternativa lateral que el sistema conserva para otros usos). Default: `'right'`. */
|
|
93
161
|
side?: 'top' | 'right' | 'bottom' | 'left';
|
|
94
|
-
/**
|
|
162
|
+
/**
|
|
163
|
+
* Título del panel. **Sin default**: sin él, sale de
|
|
164
|
+
* `consent.preferencesTitle` del `BrandMessagesProvider`.
|
|
165
|
+
*/
|
|
95
166
|
title?: ReactNode;
|
|
96
167
|
/**
|
|
97
168
|
* Nombre accesible del botón de cerrar. **Reenvío puro** al `Modal` o al
|
|
98
169
|
* `Sheet` sobre el que se abre el panel: sin él, el aspa lee `modal.close`
|
|
99
|
-
* o `sheet.close` del `BrandMessagesProvider`. El
|
|
100
|
-
*
|
|
101
|
-
* está migrado al proveedor.
|
|
170
|
+
* o `sheet.close` del `BrandMessagesProvider`. El espacio `consent` no
|
|
171
|
+
* repite la clave.
|
|
102
172
|
*/
|
|
103
173
|
closeLabel?: string;
|
|
104
|
-
/**
|
|
174
|
+
/**
|
|
175
|
+
* Marca de una categoría necesaria, solo para lectores de pantalla. **Sin
|
|
176
|
+
* default**: sin ella, sale de `consent.alwaysOn` del proveedor.
|
|
177
|
+
*/
|
|
105
178
|
alwaysOnLabel?: string;
|
|
106
179
|
/**
|
|
107
180
|
* Nodo DOM donde montar el portal del panel, reenviado a `Modal`/`Sheet`.
|
|
@@ -1,19 +1,32 @@
|
|
|
1
1
|
import { type ButtonBaseProps } from '../../atoms/Button/Button';
|
|
2
2
|
import './NotificationButton.css';
|
|
3
|
+
/**
|
|
4
|
+
* El cromo de la campana, y **solo el cromo**: cómo se llama el botón, con
|
|
5
|
+
* contador y sin él. La campana no dice nada más —el número lo pinta el
|
|
6
|
+
* `NumberBadge` y las notificaciones viven en otro sitio—, así que los dos
|
|
7
|
+
* textos son todo lo que emite por su cuenta, y ninguno cambia de pantalla a
|
|
8
|
+
* pantalla.
|
|
9
|
+
*/
|
|
10
|
+
export interface NotificationButtonMessages {
|
|
11
|
+
/** Nombre accesible cuando no hay contador. */
|
|
12
|
+
label: string;
|
|
13
|
+
/** Nombre accesible cuando hay contador. Recibe el número. */
|
|
14
|
+
countLabel: (count: number) => string;
|
|
15
|
+
}
|
|
3
16
|
export interface NotificationButtonProps extends Omit<ButtonBaseProps, 'variant' | 'iconOnly' | 'children' | 'href' | 'size'> {
|
|
4
17
|
/** Notificaciones sin leer. Con 0 (o sin él) no hay contador. */
|
|
5
18
|
count?: number;
|
|
6
19
|
/** Tope del contador («99+»). */
|
|
7
20
|
max?: number;
|
|
8
21
|
/**
|
|
9
|
-
* Nombre accesible cuando no hay contador.
|
|
10
|
-
*
|
|
22
|
+
* Nombre accesible cuando no hay contador. **Sin default**: sin ella, sale
|
|
23
|
+
* de `notificationButton.label` del `BrandMessagesProvider`.
|
|
11
24
|
*/
|
|
12
25
|
label?: string;
|
|
13
26
|
/**
|
|
14
27
|
* Nombre accesible cuando hay contador: recibe el número, para que la frase
|
|
15
|
-
* se pueda rehacer en cualquier idioma.
|
|
16
|
-
*
|
|
28
|
+
* se pueda rehacer en cualquier idioma. **Sin default**: sin ella, sale de
|
|
29
|
+
* `notificationButton.countLabel` del proveedor.
|
|
17
30
|
*/
|
|
18
31
|
countLabel?: (count: number) => string;
|
|
19
32
|
}
|
|
@@ -1,6 +1,30 @@
|
|
|
1
1
|
import { type ReactNode } from 'react';
|
|
2
2
|
import { type PopoverChangeDetails } from '../../atoms/Popover/Popover';
|
|
3
3
|
import './NotificationPanel.css';
|
|
4
|
+
/**
|
|
5
|
+
* El cromo del panel, y **solo el cromo**: cómo se llama el panel, la marca de
|
|
6
|
+
* una fila sin leer, el vacío, los dos destinos del pie y la acción sobre el
|
|
7
|
+
* conjunto. Ninguno nombra una notificación: eso viaja en `items` y lo escribe
|
|
8
|
+
* el producto —título, cuerpo y la hora, ya formateada por el consumidor—.
|
|
9
|
+
*
|
|
10
|
+
* Los textos de la campana **no están aquí**: `label` y `countLabel` son un
|
|
11
|
+
* reenvío puro al `NotificationButton`, que tiene su propio espacio. El panel
|
|
12
|
+
* no repite la clave.
|
|
13
|
+
*/
|
|
14
|
+
export interface NotificationPanelMessages {
|
|
15
|
+
/** Nombre del panel (`role="dialog"`) y de la lista. No se pinta. */
|
|
16
|
+
panel: string;
|
|
17
|
+
/** Texto, solo para lectores de pantalla, que marca una fila sin leer. */
|
|
18
|
+
unread: string;
|
|
19
|
+
/** Mensaje cuando no hay notificaciones. */
|
|
20
|
+
empty: string;
|
|
21
|
+
/** Rótulo del enlace a la bandeja. */
|
|
22
|
+
all: string;
|
|
23
|
+
/** Rótulo del enlace a las preferencias. */
|
|
24
|
+
preferences: string;
|
|
25
|
+
/** Rótulo del botón de marcar todas como leídas. */
|
|
26
|
+
markAllRead: string;
|
|
27
|
+
}
|
|
4
28
|
/** Una notificación tal y como la enseña el panel: ya resuelta, sin datos crudos. */
|
|
5
29
|
export interface NotificationPanelItem {
|
|
6
30
|
id: string;
|
|
@@ -56,24 +80,46 @@ export interface NotificationPanelProps {
|
|
|
56
80
|
* `<a>` corriente.
|
|
57
81
|
*/
|
|
58
82
|
renderLink?: RenderNotificationPanelLink;
|
|
59
|
-
/**
|
|
83
|
+
/**
|
|
84
|
+
* Nombre accesible de la campana sin contador. **Reenvío puro** al
|
|
85
|
+
* `NotificationButton`: sin ella, sale de `notificationButton.label`.
|
|
86
|
+
*/
|
|
60
87
|
label?: string;
|
|
61
|
-
/**
|
|
88
|
+
/**
|
|
89
|
+
* Nombre accesible de la campana con contador. **Reenvío puro** al
|
|
90
|
+
* `NotificationButton`: sin ella, sale de `notificationButton.countLabel`.
|
|
91
|
+
*/
|
|
62
92
|
countLabel?: (count: number) => string;
|
|
63
93
|
/**
|
|
64
94
|
* Nombre del panel (`role="dialog"`) y de la lista. **No se pinta**: el
|
|
65
|
-
* panel no lleva cabecera visible.
|
|
95
|
+
* panel no lleva cabecera visible. **Sin default**: sin él, sale de
|
|
96
|
+
* `notificationPanel.panel` del `BrandMessagesProvider`.
|
|
66
97
|
*/
|
|
67
98
|
panelLabel?: string;
|
|
68
|
-
/**
|
|
99
|
+
/**
|
|
100
|
+
* Texto solo para lectores de pantalla que marca una fila sin leer. **Sin
|
|
101
|
+
* default**: sin él, sale de `notificationPanel.unread`.
|
|
102
|
+
*/
|
|
69
103
|
unreadLabel?: string;
|
|
70
|
-
/**
|
|
104
|
+
/**
|
|
105
|
+
* Mensaje cuando no hay notificaciones. **Sin default**: sin él, sale de
|
|
106
|
+
* `notificationPanel.empty`.
|
|
107
|
+
*/
|
|
71
108
|
emptyLabel?: string;
|
|
72
|
-
/**
|
|
109
|
+
/**
|
|
110
|
+
* Rótulo del enlace a la bandeja. **Sin default**: sin él, sale de
|
|
111
|
+
* `notificationPanel.all`.
|
|
112
|
+
*/
|
|
73
113
|
allLabel?: string;
|
|
74
|
-
/**
|
|
114
|
+
/**
|
|
115
|
+
* Rótulo del enlace a las preferencias. **Sin default**: sin él, sale de
|
|
116
|
+
* `notificationPanel.preferences`.
|
|
117
|
+
*/
|
|
75
118
|
preferencesLabel?: string;
|
|
76
|
-
/**
|
|
119
|
+
/**
|
|
120
|
+
* Rótulo del botón de marcar todas. **Sin default**: sin él, sale de
|
|
121
|
+
* `notificationPanel.markAllRead`. Solo se lee cuando el botón se pinta.
|
|
122
|
+
*/
|
|
77
123
|
markAllReadLabel?: string;
|
|
78
124
|
open?: boolean;
|
|
79
125
|
defaultOpen?: boolean;
|
|
@@ -1,5 +1,29 @@
|
|
|
1
1
|
import { type ReactNode } from 'react';
|
|
2
2
|
import './FloatingDock.css';
|
|
3
|
+
/**
|
|
4
|
+
* El cromo del dock, y **solo el cromo**: el aspa y cómo se lee el contador.
|
|
5
|
+
*
|
|
6
|
+
* **Tiene espacio propio y no hereda `modal.close`, a propósito.** El panel del
|
|
7
|
+
* dock no es un `Modal` y no puede serlo: es un diálogo **no modal**
|
|
8
|
+
* (`modal={false}`) sin velo, que no atrapa el foco y deja la página viva
|
|
9
|
+
* detrás, montado por portal **dentro del propio ancla** para heredar por
|
|
10
|
+
* cascada la superficie donde el consumidor lo haya puesto, anclado a una
|
|
11
|
+
* esquina de la ventana y con el aspa fuera de la cabecera. El `Modal` del
|
|
12
|
+
* sistema es lo contrario en las cuatro cosas —velo, foco atrapado, portal en
|
|
13
|
+
* `document.body`, diálogo centrado—, así que hacerlo pasar por él no era
|
|
14
|
+
* quitar una clave: era cambiar el componente. La razón es real y de hoy, no
|
|
15
|
+
* histórica, y por eso el aspa se llama aquí.
|
|
16
|
+
*
|
|
17
|
+
* El `label` del lanzador **no está aquí**: nombra lo que abre el dock —«Abrir
|
|
18
|
+
* el asistente», «Abrir el chat de soporte»— y eso lo sabe el producto. Sigue
|
|
19
|
+
* siendo prop obligatoria.
|
|
20
|
+
*/
|
|
21
|
+
export interface FloatingDockMessages {
|
|
22
|
+
/** Nombre accesible del aspa que cierra el panel. */
|
|
23
|
+
close: string;
|
|
24
|
+
/** Cómo se lee el contador de novedades. Interpola el número. */
|
|
25
|
+
badge: (count: number) => string;
|
|
26
|
+
}
|
|
3
27
|
/** Esquina de la ventana a la que se ancla el dock. */
|
|
4
28
|
export type FloatingDockPosition = 'bottom-end' | 'bottom-start' | 'top-end' | 'top-start';
|
|
5
29
|
export interface FloatingDockProps extends Omit<React.ComponentPropsWithoutRef<'div'>, 'title' | 'children'> {
|
|
@@ -26,15 +50,21 @@ export interface FloatingDockProps extends Omit<React.ComponentPropsWithoutRef<'
|
|
|
26
50
|
defaultOpen?: boolean;
|
|
27
51
|
/** Se llama al abrirse y al cerrarse, en controlado y en no controlado. */
|
|
28
52
|
onOpenChange?: (open: boolean) => void;
|
|
29
|
-
/**
|
|
53
|
+
/**
|
|
54
|
+
* `aria-label` del aspa. **Sin default**: sin él, sale de
|
|
55
|
+
* `floatingDock.close` del `BrandMessagesProvider` — espacio propio, no
|
|
56
|
+
* `modal.close`: el panel del dock no es un `Modal` (ver
|
|
57
|
+
* `FloatingDockMessages`).
|
|
58
|
+
*/
|
|
30
59
|
closeLabel?: string;
|
|
31
60
|
/** Novedades sin ver. Con 0 (o sin él) no hay contador. */
|
|
32
61
|
badge?: number;
|
|
33
62
|
/** Tope del contador («99+»). */
|
|
34
63
|
badgeMax?: number;
|
|
35
64
|
/**
|
|
36
|
-
* Cómo se lee el contador.
|
|
37
|
-
* Interpola el número, así que es una
|
|
65
|
+
* Cómo se lee el contador. **Sin default**: sin ella, sale de
|
|
66
|
+
* `floatingDock.badge` del proveedor. Interpola el número, así que es una
|
|
67
|
+
* función. Solo se lee cuando hay contador y `badgeLive`.
|
|
38
68
|
*/
|
|
39
69
|
badgeLabel?: (count: number) => string;
|
|
40
70
|
/**
|