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 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. `onChange` recibe el código de la opción siguiente; qué hacer con eso
193
- (`i18n.changeLanguage`, `next-intl`, lo que sea) es del producto — la librería no sabe
194
- qué mecanismo de traducción hay atrás.
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kaizen-lib",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "description": "Tokens y componentes compartidos entre los fronts de Kaizen.",
5
5
  "license": "MIT",
6
6
  "author": "Nicolás Sandobal",
@@ -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={`Cambiar idioma a ${siguiente.nombre}`} onClick={() => onChange(siguiente.codigo)}>
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, $glow }) =>
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) se pisa con `style`, ya que `--kz-icon-on` es una
98
- * variable CSS común y un estilo en línea le gana a la de acá:
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
- * glow
104
- * style={{ '--kz-icon-on': 'var(--mi-color-de-marca)' } as React.CSSProperties}
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 />