kaizen-lib 0.2.2 → 0.2.4
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/README.md +7 -3
- package/package.json +1 -1
- package/src/ui/LangSelector.tsx +12 -2
- package/src/ui/ReactionButton.tsx +11 -6
package/README.md
CHANGED
|
@@ -177,6 +177,7 @@ import { LangSelector } from 'kaizen-lib/ui';
|
|
|
177
177
|
<LangSelector
|
|
178
178
|
actual={i18n.language}
|
|
179
179
|
onChange={(codigo) => i18n.changeLanguage(codigo)}
|
|
180
|
+
etiqueta={(siguiente) => `Cambiar idioma a ${siguiente.nombre}`}
|
|
180
181
|
opciones={[
|
|
181
182
|
{ codigo: 'es', nombre: 'Español', icono: <ReactCountryFlag countryCode="AR" /> },
|
|
182
183
|
{ codigo: 'en', nombre: 'English', icono: <ReactCountryFlag countryCode="US" /> },
|
|
@@ -189,9 +190,12 @@ cada tap y muestra el ícono del idioma actual, con el relieve de `Relieve` ya i
|
|
|
189
190
|
(a diferencia de `IconButton`, acá no hace falta envolverlo aparte — todo consumidor lo
|
|
190
191
|
quería). No trae banderas — mismo criterio que `IconButton` no trae íconos —, así que
|
|
191
192
|
`icono` es cosa del producto; `react-country-flag` (ya usado en tuxon-front) es una
|
|
192
|
-
opción probada. `
|
|
193
|
-
|
|
194
|
-
|
|
193
|
+
opción probada. `etiqueta` arma el nombre accesible a partir de la opción siguiente y la
|
|
194
|
+
actual — es obligatoria y sin default: la frase y el idioma son del producto (dos ya
|
|
195
|
+
difieren: uno anuncia el destino, "Cambiar idioma a English"; otro el estado, "Idioma:
|
|
196
|
+
English. Cambiar idioma"). `onChange` recibe el código de la opción siguiente; qué hacer
|
|
197
|
+
con eso (`i18n.changeLanguage`, `next-intl`, lo que sea) es del producto — la librería no
|
|
198
|
+
sabe qué mecanismo de traducción hay atrás.
|
|
195
199
|
|
|
196
200
|
## Desarrollo
|
|
197
201
|
|
package/package.json
CHANGED
package/src/ui/LangSelector.tsx
CHANGED
|
@@ -24,6 +24,15 @@ type Props<T extends string> = {
|
|
|
24
24
|
opciones: OpcionDeIdioma<T>[];
|
|
25
25
|
actual: T;
|
|
26
26
|
onChange: (codigo: T) => void;
|
|
27
|
+
/**
|
|
28
|
+
* Arma el nombre accesible del botón, a partir de la opción actual y la
|
|
29
|
+
* siguiente (a la que se pasa al tocar). Obligatoria y sin default: qué
|
|
30
|
+
* frase decir y en qué idioma es del producto, la librería no lo fija.
|
|
31
|
+
* Dos productos ya difieren en esto mismo — uno anuncia el destino
|
|
32
|
+
* ("Cambiar idioma a English"), otro el estado ("Idioma: English. Cambiar
|
|
33
|
+
* idioma") — y los dos son legítimos.
|
|
34
|
+
*/
|
|
35
|
+
etiqueta: (siguiente: OpcionDeIdioma<T>, actual: OpcionDeIdioma<T>) => string;
|
|
27
36
|
size?: IconButtonSize;
|
|
28
37
|
};
|
|
29
38
|
|
|
@@ -45,6 +54,7 @@ type Props<T extends string> = {
|
|
|
45
54
|
* <LangSelector
|
|
46
55
|
* actual={idiomaActual}
|
|
47
56
|
* onChange={(codigo) => i18n.changeLanguage(codigo)}
|
|
57
|
+
* etiqueta={(siguiente) => `Cambiar idioma a ${siguiente.nombre}`}
|
|
48
58
|
* opciones={[
|
|
49
59
|
* { codigo: 'es', nombre: 'Español', icono: <ReactCountryFlag countryCode="AR" /> },
|
|
50
60
|
* { codigo: 'en', nombre: 'English', icono: <ReactCountryFlag countryCode="US" /> },
|
|
@@ -52,7 +62,7 @@ type Props<T extends string> = {
|
|
|
52
62
|
* />
|
|
53
63
|
* ```
|
|
54
64
|
*/
|
|
55
|
-
export function LangSelector<T extends string>({ opciones, actual, onChange, size = 'lg' }: Props<T>) {
|
|
65
|
+
export function LangSelector<T extends string>({ opciones, actual, onChange, etiqueta, size = 'lg' }: Props<T>) {
|
|
56
66
|
// Sin opciones no hay nada que ciclar. Defensivo: un selector de idioma
|
|
57
67
|
// vacío es un error de uso, no algo que la librería deba explicar.
|
|
58
68
|
if (opciones.length === 0) return null;
|
|
@@ -63,7 +73,7 @@ export function LangSelector<T extends string>({ opciones, actual, onChange, siz
|
|
|
63
73
|
|
|
64
74
|
return (
|
|
65
75
|
<Relieve>
|
|
66
|
-
<IconButton size={size} label={
|
|
76
|
+
<IconButton size={size} label={etiqueta(siguiente, opcionActual)} onClick={() => onChange(siguiente.codigo)}>
|
|
67
77
|
{opcionActual.icono}
|
|
68
78
|
</IconButton>
|
|
69
79
|
</Relieve>
|
|
@@ -55,11 +55,11 @@ const Raiz = styled.button<{ $size: IconButtonSize; $tone: ReactionTone; $active
|
|
|
55
55
|
padding: 0 8px;
|
|
56
56
|
background: transparent;
|
|
57
57
|
--kz-icon-on: ${({ $tone }) => TONO_ENCENDIDO[$tone]};
|
|
58
|
+
--kz-icon-glow: ${({ $glow }) => ($glow ? '0 0 8px color-mix(in srgb, currentColor 65%, transparent)' : 'none')};
|
|
58
59
|
color: ${({ $active }) => ($active ? 'var(--kz-icon-on)' : fg.muted)};
|
|
59
60
|
|
|
60
61
|
svg {
|
|
61
|
-
filter: ${({ $active
|
|
62
|
-
$active && $glow ? 'drop-shadow(0 0 8px color-mix(in srgb, currentColor 65%, transparent))' : 'none'};
|
|
62
|
+
filter: ${({ $active }) => ($active ? 'drop-shadow(var(--kz-icon-glow))' : 'none')};
|
|
63
63
|
transition: filter ${motion.fast} ${motion.ease};
|
|
64
64
|
}
|
|
65
65
|
|
|
@@ -94,14 +94,19 @@ const Cuenta = styled.span`
|
|
|
94
94
|
* ```
|
|
95
95
|
*
|
|
96
96
|
* Un color que los seis tonos no cubren (la marca de un producto, no una
|
|
97
|
-
* categoría semántica)
|
|
98
|
-
*
|
|
97
|
+
* categoría semántica), o una intensidad de resplandor propia y no la
|
|
98
|
+
* genérica de `glow`, se pisan con `style`: `--kz-icon-on` y `--kz-icon-glow`
|
|
99
|
+
* son variables CSS comunes, y un estilo en línea le gana a la de acá por
|
|
100
|
+
* origen, no por especificidad — no hace falta tocar la librería por un
|
|
101
|
+
* color o un resplandor que ningún otro consumidor va a repetir.
|
|
99
102
|
*
|
|
100
103
|
* ```tsx
|
|
101
104
|
* <ReactionButton
|
|
102
105
|
* active={guardado}
|
|
103
|
-
*
|
|
104
|
-
*
|
|
106
|
+
* style={{
|
|
107
|
+
* '--kz-icon-on': 'var(--mi-color-de-marca)',
|
|
108
|
+
* '--kz-icon-glow': '0 0 8px rgba(255, 77, 109, 0.75)',
|
|
109
|
+
* } as React.CSSProperties}
|
|
105
110
|
* label="Guardar"
|
|
106
111
|
* >
|
|
107
112
|
* <IconoMarcador />
|