@studiolxd/brand 49.18.0 → 49.20.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 +206 -0
- package/dist/_shared/tokens.js +59 -0
- package/dist/_types/atoms/Autocomplete/Autocomplete.d.ts +83 -0
- package/dist/_types/atoms/List/List.d.ts +20 -0
- package/dist/_types/atoms/NumberInput/NumberInput.d.ts +33 -2
- package/dist/_types/atoms/Text/Text.d.ts +12 -2
- package/dist/_types/molecules/AutocompleteField/AutocompleteField.d.ts +57 -0
- package/dist/_types/molecules/NumberInputField/NumberInputField.d.ts +14 -2
- package/dist/autocomplete-field.css +1 -0
- package/dist/autocomplete-field.js +58 -0
- package/dist/autocomplete.css +1 -0
- package/dist/autocomplete.js +145 -0
- package/dist/brand.css +122 -1
- package/dist/list.css +1 -1
- package/dist/list.js +39 -13
- package/dist/number-input-field.js +22 -19
- package/dist/number-input.css +1 -1
- package/dist/number-input.js +54 -44
- package/dist/text.css +1 -1
- package/dist/text.js +6 -5
- package/dist/toaster.js +23 -20
- package/dist/tokens.css +126 -1
- package/package.json +16 -2
- package/src/tokens/components/autocomplete.css +40 -0
- package/src/tokens/components/autocomplete.ts +7 -0
- package/src/tokens/components/number-input.css +5 -0
- package/src/tokens/components/number-input.ts +2 -2
- package/src/tokens/components/text-inline.css +3 -0
- package/src/tokens/components/text.css +9 -0
- package/src/tokens/index.css +2 -0
- package/src/tokens/molecules/autocomplete-field.css +17 -0
- package/src/tokens/molecules/autocomplete-field.ts +8 -0
- package/src/tokens/scss/_index.legacy.scss +2 -0
- package/src/tokens/scss/_index.scss +2 -0
- package/src/tokens/scss/components/_autocomplete.scss +37 -0
- package/src/tokens/scss/components/_number-input.scss +5 -0
- package/src/tokens/scss/components/_text-inline.scss +2 -0
- package/src/tokens/scss/components/_text.scss +7 -0
- package/src/tokens/scss/molecules/_autocomplete-field.scss +14 -0
- package/src/tokens/surface-dark-derived.css +12 -0
- package/src/tokens/surface-invert.css +15 -0
- package/src/tokens/surface-light.css +15 -0
- package/src/tokens/surface-public.css +7 -0
- package/src/tokens/tokens.json +59 -0
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,212 @@ 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.20.0] — 2026-10-04
|
|
11
|
+
|
|
12
|
+
> **Minor.** `Autocomplete` y `AutocompleteField` (sugerencias con texto libre), tachado en `Text`, y `NumberInput`
|
|
13
|
+
> con `commitMode` (avisar al terminar) y `compact` (para filas de lista), en React, SwiftUI y Compose donde hay
|
|
14
|
+
> versión nativa. Arreglos: color del texto de las filas en Compose y pruebas estables. Todo opcional: sin las props
|
|
15
|
+
> nuevas el HTML no cambia.
|
|
16
|
+
|
|
17
|
+
### Arreglos previos a 49.20.0 (patch)
|
|
18
|
+
|
|
19
|
+
- **Compose · `BrandListItem(text, subtitle)`** pintaba su texto con `BasicText(style = LocalBrandTextStyle.current)`, cuyo
|
|
20
|
+
color es `Unspecified` (negro de Compose): fuera de un `BrandList` salía oscuro sobre fondo oscuro. Ahora la fila fija
|
|
21
|
+
su propia tinta y tipografía (`text.list.*`, `ProvideBrandContent`), como `BrandList`, SwiftUI y React. `BrandSheet`,
|
|
22
|
+
`BrandEmptyState` y `BrandConfirmDialog` no tenían el fallo: pintan sus textos con tokens explícitos y `BrandText`
|
|
23
|
+
cae a la tinta del esquema. Captura nueva `listItemOutsideList` (claro y oscuro).
|
|
24
|
+
- **Storybook · pruebas del Toast** («Test — tintas del aviso en claro/oscuro»): la prueba, no el componente, estaba mal.
|
|
25
|
+
Tras `toast.dismiss()` buscaba `.toast` y recogía el aviso anterior, aún montado mientras sale; al desmontarse
|
|
26
|
+
`getComputedStyle` devolvía `''` en fondo y tinta. Ahora localiza el aviso por su título y mide las tintas de una vez.
|
|
27
|
+
- **Storybook · `test:stories`**: el proyecto `storybook` declara `testTimeout`/`hookTimeout` de 60 s. «Trigger de
|
|
28
|
+
icono» del `Menu` (una story solo de render) agotó los 20 s por carga de la máquina, sin fallo real.
|
|
29
|
+
- **SwiftUI · captura `phrase-mismatch` en macOS** inestable: el `onAppear` daba el foco al campo de la frase y, según
|
|
30
|
+
llegara antes o después de la fotografía, salían el anillo de foco y el cursor. Las capturas ya no piden foco
|
|
31
|
+
(`autofocus: false` en el `init` interno con estado); el componente público lo sigue haciendo.
|
|
32
|
+
Las referencias de iOS de esa captura se regrabaron sin foco (antes llevaban el anillo).
|
|
33
|
+
- **Compose · captura `NumberInputField` `compact`** desfasada (1 px de desplazamiento vertical respecto al render actual;
|
|
34
|
+
regrabada dos veces con bytes idénticos, así que es determinista): referencia actualizada.
|
|
35
|
+
- **Compose · título de `BrandSheet`**: comparado con React y SwiftUI, los tres leen el mismo token
|
|
36
|
+
(`sheet.title-font-size` → `modal.title-font-size`, 32 px, peso 500): no hay diferencia y no se cambia nada.
|
|
37
|
+
|
|
38
|
+
### NumberInput — confirmar al terminar, compacto y etiquetas tipadas (minor)
|
|
39
|
+
|
|
40
|
+
Todo opcional y en las tres plataformas a la vez (React, SwiftUI y Compose): sin las props nuevas, el comportamiento y
|
|
41
|
+
el HTML son los de 49.19.0 (pruebas en `NumberInput.test.tsx` y `NumberInputField.test.tsx`).
|
|
42
|
+
|
|
43
|
+
- **`commitMode: 'change' | 'blur'`** (por defecto `'change'`). Con `'blur'`, lo escrito a mano se avisa **una sola
|
|
44
|
+
vez**, al salir del campo o con Enter (y solo si cambia el valor); Escape descarta lo escrito y vuelve al último
|
|
45
|
+
valor. Los botones − y + avisan al momento en los dos modos. Con `onEmpty`, vaciar y confirmar es «sin valor». Motivo:
|
|
46
|
+
una fila de lista donde cada aviso es una escritura en un servidor. Nativo: `commitMode: .blur` (SwiftUI, al perder el
|
|
47
|
+
foco o con «intro»; Escape con teclado físico) y `NumberInputCommitMode.Blur` (Compose, al perder el foco o «Hecho»).
|
|
48
|
+
- **`compact`** (booleano). Variante para filas de lista, `trailing` de `ListItem`: 32 de alto, botones de 24 y cifra
|
|
49
|
+
de 40 de ancho, sin estirarse; manda sobre `size`. En nativo la zona táctil sigue llegando a 44 pt / 48 dp. Tokens
|
|
50
|
+
nuevos `number-input.compact-*` (`height`, `font-size`, `btn-width`, `field-width`, `padding-inline`).
|
|
51
|
+
- **`decrementLabel` / `incrementLabel`** declaradas en `NumberInputFieldProps` (ya llegaban al control en ejecución
|
|
52
|
+
pero no estaban en el tipo). En nativo ya existían.
|
|
53
|
+
- Fichas de paridad, capturas y pareja con Storybook (`native/apple/Comparisons/NumberInputField/compacto.*`) al día.
|
|
54
|
+
|
|
55
|
+
> **Minor.** Dos piezas de la primera app web (Homenize): un autocompletado que admite texto libre y el tachado en
|
|
56
|
+
> `Text` (React, SwiftUI y Compose). Todo opcional: sin las props nuevas, el HTML de `Text` es idéntico al anterior.
|
|
57
|
+
|
|
58
|
+
### React — `Autocomplete` y `AutocompleteField` (minor)
|
|
59
|
+
|
|
60
|
+
Campo de texto con sugerencias donde **el valor es el texto escrito**: se elige una sugerencia o se queda lo que no
|
|
61
|
+
coincide con ninguna. Componente propio y no una prop de `AsyncSelect` porque el modelo de valor es otro
|
|
62
|
+
(`AsyncSelect` guarda un identificador y pide `selectedOption`; aquí `value` es un `string`).
|
|
63
|
+
|
|
64
|
+
- **API**: `value`/`defaultValue`/`onValueChange` (texto), `onSelect(option)` (solo al elegir: para saber cuál fue),
|
|
65
|
+
sugerencias síncronas con `options` (filtradas por el control, sin mayúsculas ni tildes) o síncronas/asíncronas con
|
|
66
|
+
`onSearch` (con `debounceMs`, descarte de respuestas fuera de orden y `minChars`). `name` y `required` nativos en el
|
|
67
|
+
propio `<input>`.
|
|
68
|
+
- **Combobox ARIA con lista**: `aria-activedescendant`, `aria-expanded`, `aria-controls` solo con la lista abierta;
|
|
69
|
+
flechas, Enter (sin sugerencia marcada se queda con lo escrito y deja enviar el formulario), Escape (no sube al
|
|
70
|
+
`Modal`) y Tab.
|
|
71
|
+
- **Sin texto propio**: sin sugerencias no hay lista ni «sin resultados». No toca el catálogo de mensajes.
|
|
72
|
+
- Misma cara que `AsyncSelect`/`SelectField` por tokens nuevos `autocomplete.*` y `autocomplete-field.*` que apuntan a
|
|
73
|
+
los de `async-select*` (el par oscuro llega por `surface-dark-derived.css`). Sin `surface-dark-*` propios.
|
|
74
|
+
- Registrados: `entry-points.mjs` (+ `clientComponents`), `package.json › exports` (`./autocomplete`,
|
|
75
|
+
`./autocomplete-field`) y `src/index.ts`. Historias, docs `.mdx` y pruebas con teclado.
|
|
76
|
+
- Anotado en `native/README.md` como candidato a versión nativa si una app lo pide.
|
|
77
|
+
|
|
78
|
+
### `Text` — tachado (minor)
|
|
79
|
+
|
|
80
|
+
- **React**: prop `strikethrough` y `as="del"`/`as="s"` (que tachan por sí solos). Clase nueva `text--strikethrough`,
|
|
81
|
+
tinta atenuada (`text-inline.strikethrough-color`, la del texto secundario) y grosor del sistema
|
|
82
|
+
(`text-inline.strikethrough-thickness`); con un `tone` manda el del tono. Sin la prop, el HTML es idéntico
|
|
83
|
+
(prueba en `Text.test.tsx`).
|
|
84
|
+
- **SwiftUI y Compose**: `strikethrough:` / `TextElement.del` y `.s` en `BrandText` / `Text.brand(…)` /
|
|
85
|
+
`brandSpanStyle`, con la ficha de paridad al día.
|
|
86
|
+
|
|
87
|
+
## [49.19.0] — 2026-10-04
|
|
88
|
+
|
|
89
|
+
> **Minor.** Primera versión con **Brand nativo**: tokens, fuentes y componentes para SwiftUI (iOS 17 y macOS 14) y
|
|
90
|
+
> Jetpack Compose (Android), fuera del paquete npm. En React, opciones de fila en `List`/`ListItem`
|
|
91
|
+
> (`showSeparators`, `secondary`, `trailing`, `leading`), `NumberInput` sin valor y el aviso neutro de `Toast` en
|
|
92
|
+
> oscuro. Todo opcional: sin las props nuevas el HTML no cambia.
|
|
93
|
+
|
|
94
|
+
### React — huecos de la primera app nativa (minor)
|
|
95
|
+
|
|
96
|
+
Todo opcional: sin las props nuevas el HTML es idéntico al anterior (pruebas con `toMatchInlineSnapshot` generadas
|
|
97
|
+
contra el código de antes, en `InputField.test.tsx`, `NumberInputField.test.tsx`, `PasswordField.test.tsx` y
|
|
98
|
+
`List.test.tsx`).
|
|
99
|
+
|
|
100
|
+
- **`ListItem` `leading`** (`ReactNode`): accesorio al principio de la fila (icono, avatar), pareja de `trailing`.
|
|
101
|
+
Clase nueva `list__item-leading`; story «Filas con accesorio inicial».
|
|
102
|
+
- **`NumberInput` / `NumberInputField` sin valor**: `value` y `defaultValue` admiten `null` (campo vacío), y la prop
|
|
103
|
+
nueva `onEmpty` hace que vaciar el texto sea un valor (sin ella, el campo se comporta como siempre y recupera el
|
|
104
|
+
último número al salir). Desde vacío, − y + cuentan como si el campo valiera 0 y se ajustan a `min`/`max`.
|
|
105
|
+
Story «Sin valor».
|
|
106
|
+
- **Foco desde fuera**: el `ref` ya llegaba al `<input>` en `InputField` (también en búsqueda), `NumberInputField` y
|
|
107
|
+
`PasswordField` (y al botón en `SelectField`); ahora lo fija una prueba y lo documenta `InputField.mdx`.
|
|
108
|
+
- **Documentación de `Button`**: la nota de `destructive` decía que valía para `primary` y `ghost` y mencionaba una
|
|
109
|
+
variante `accent` que ya no existe; en el código solo `outline` y `text` tienen estilo destructivo. Corregida la
|
|
110
|
+
nota (y `destructiveIntent` → `destructive`); el comportamiento no cambia.
|
|
111
|
+
|
|
112
|
+
### Nativo — huecos de la primera app nativa (minor)
|
|
113
|
+
|
|
114
|
+
- **Generador de Kotlin**: los tamaños de letra de componente cuyo nombre no termina en `font-size`
|
|
115
|
+
(`empty-state.title-font-size-sm`, `description-font-size-sm`) salían como `Dp`; ahora todo token con `font-size`
|
|
116
|
+
como segmento es `TextUnit` (`sp`). `BrandEmptyState` deja de convertir a mano. Grupo `password-field` en
|
|
117
|
+
`NATIVE_COMPONENT_GROUPS`.
|
|
118
|
+
- **`PasswordField` nativo** (`BrandPasswordField`, SwiftUI y Compose): campo con ojo de mostrar u ocultar, etiqueta
|
|
119
|
+
oculta por defecto como en React, textos accesibles como parámetros. Ficha, capturas y parejas. **Compose: `BrandInputField`
|
|
120
|
+
pierde el ojo** (`passwordToggle`, `showPasswordLabel`, `hidePasswordLabel`; breaking menor en Compose): quien lo quiera usa `BrandPasswordField`.
|
|
121
|
+
- **Foco desde fuera** en `InputField`, `NumberInputField` y `PasswordField`: SwiftUI `.brandFocused($focus)` /
|
|
122
|
+
`.brandFocused($focus, equals:)` (un `FocusState` de la app aplicado al texto, no a los botones); Compose
|
|
123
|
+
`focusRequester: FocusRequester?`.
|
|
124
|
+
- **`NumberInputField` sin valor**: `Binding<Double?>` (SwiftUI; `Binding<Double>` sigue compilando) y `value: Double?`
|
|
125
|
+
(Compose; **cambio de firma**: `onValueChange` recibe `Double?`). Vacío = `nil`/`null`, con `placeholder` y
|
|
126
|
+
«Sin valor» (`emptyValueLabel`) para lectores de pantalla; desde vacío − y + parten de 0 y respetan `min`/`max`.
|
|
127
|
+
- **`BrandListItem`**: `leading` en SwiftUI (ya existía en Compose) y `action` (fila pulsable entera) en SwiftUI, pareja de
|
|
128
|
+
`onClick` de Compose, anotado en la ficha como diferencia deliberada.
|
|
129
|
+
|
|
130
|
+
### Nativo — Jetpack Compose con los mismos componentes que SwiftUI (minor)
|
|
131
|
+
|
|
132
|
+
- **Tokens de componente en Kotlin**: `pnpm build:tokens` genera `BrandComponentTokens.kt` (mismos grupos y reglas que
|
|
133
|
+
`BrandComponentTokens.swift`; los colores y las medidas con par `surface-dark-*` salen como `BrandSchemeValue`, que se
|
|
134
|
+
resuelve con `.current`). El Swift generado no cambia.
|
|
135
|
+
- **Componentes de Compose** (`native/android/brand`): `BrandButton`, `BrandCloseButton`, `BrandHeading`/`BrandParagraph`/
|
|
136
|
+
`BrandText`, `BrandIcon` (77 iconos; `pnpm build:native-icons` genera también `BrandIconData.kt`), `BrandInputField`,
|
|
137
|
+
`BrandNumberInputField`, `BrandSelectField`, `BrandSwitcherField`, `BrandToggleGroup`, `BrandThemeSwitcher`,
|
|
138
|
+
`BrandList`/`BrandListItem` (`showSeparators`, `secondary`, `trailing`), `BrandTag`, `BrandEmptyState`, `BrandSkeleton`,
|
|
139
|
+
`BrandSheet`, `BrandConfirmDialog` y `ToastCenter`/`ToastHost`, con estados, escala de fuente, TalkBack, 48 dp, claro y
|
|
140
|
+
oscuro y «quitar animaciones». Cumplen las fichas de paridad (`ParityTest`), con capturas Paparazzi en claro y oscuro,
|
|
141
|
+
`@Preview` y parejas React ↔ Compose en `native/android/Comparisons/`. Sin dependencias nuevas en producción.
|
|
142
|
+
- Fichas de paridad: líneas «Android: …» en `differences`. `native/README.md` documenta los componentes de Compose.
|
|
143
|
+
|
|
144
|
+
### React — `List`/`ListItem` (minor) y `Toast` (patch)
|
|
145
|
+
|
|
146
|
+
- **`List` `showSeparators`** (default `false`): línea entre filas con los tokens `separator.*`
|
|
147
|
+
(clase nueva `list--separated`). **`ListItem` `secondary` y `trailing`** (`ReactNode`): línea
|
|
148
|
+
menor atenuada y accesorio al final de la fila. Sin las props nuevas el HTML es exactamente el
|
|
149
|
+
de antes; la estructura de fila (`list__item-row`, `list__item-main`, `list__item-secondary`,
|
|
150
|
+
`list__item-trailing`) solo aparece al usarlas. Tokens nuevos en `text.list.*`
|
|
151
|
+
(`item-gap`, `secondary-*`, `separator-*`, con pares `surface-dark-*`). Prueba que fija el HTML
|
|
152
|
+
anterior en `List.test.tsx`.
|
|
153
|
+
- **Fix `Toast` neutro en oscuro**: el neutro (y `info`/`loading`) llevaba `.surface-dark` en la
|
|
154
|
+
raíz, que volteaba su relleno a blanco incluso sobre página clara y dejaba el aspa en blanco
|
|
155
|
+
sobre blanco. Ahora, como el `Alert`, la raíz solo la lleva `success`/`error`, y el interior
|
|
156
|
+
(contenido y aspa) lleva `.surface-invert` (neutro) o `.surface-light` (`warning`).
|
|
157
|
+
|
|
158
|
+
### Nativo
|
|
159
|
+
|
|
160
|
+
- `BrandList(showsSeparators:)` pasa a **`showSeparators:`** (mismo nombre que en React; lo nativo
|
|
161
|
+
aún no se había publicado). `BrandListItem` `secondary`/`trailing` y los separadores leen ahora
|
|
162
|
+
los tokens `text.list.*`. Fichas `List.json`/`ListItem.json` actualizadas: ya no son «solo nativo».
|
|
163
|
+
|
|
164
|
+
> Infraestructura nativa (iOS, macOS y Android). **No cambia nada del paquete npm**: lo
|
|
165
|
+
> nativo vive en `native/`, `Package.swift` y `jitpack.yml`, fuera de `package.json#files`.
|
|
166
|
+
|
|
167
|
+
### Nativo — nuevo (`native/`)
|
|
168
|
+
|
|
169
|
+
- **Tokens nativos con Style Dictionary**: dos plataformas nuevas en `sd.config.mjs`
|
|
170
|
+
(`swift`, `kotlin`) con formatos propios en `sd.formats.mjs`. `pnpm build:tokens`
|
|
171
|
+
genera `native/apple/…/Tokens/BrandTokens.swift` y
|
|
172
|
+
`native/android/…/tokens/BrandTokens.kt`: colores y roles claro/oscuro (`Color` dinámico
|
|
173
|
+
en Swift; `BrandColorRoles.light|dark` + `CompositionLocal` en Compose), espaciado,
|
|
174
|
+
radios, anchos de borde, tallas, opacidad, tipografía, sombras y motion. Los tokens de
|
|
175
|
+
componente quedan para cuando se porte cada componente.
|
|
176
|
+
- **Paquete SwiftPM** `StudiolxdBrand` (`Package.swift` en la raíz; iOS 17, macOS 14,
|
|
177
|
+
Swift 6): `registerFonts()`, `BrandTextStyle` y `Font.brand(_:)`.
|
|
178
|
+
- **Módulo Gradle** `com.github.studiolxd:brand` (`native/android`, minSdk 26, publicable
|
|
179
|
+
por JitPack con `jitpack.yml`): `BrandTheme`, `BrandFontFamily`, `BrandTypography`.
|
|
180
|
+
- **Fuentes**: `pnpm build:native-fonts` descomprime los woff2 variables (Google Sans Flex,
|
|
181
|
+
Google Sans Code, Libre Bodoni; cara `latin`) a TTF para ambas plataformas.
|
|
182
|
+
- **Paridad con React**: fichas por componente (`native/parity/`), validador
|
|
183
|
+
(`pnpm native:parity`, con TypeScript y `ajv`) y pruebas de paridad en Swift y Kotlin.
|
|
184
|
+
- **Capturas**: swift-snapshot-testing (Apple) y Paparazzi (Android), con una captura de
|
|
185
|
+
ejemplo de los roles de color en claro y oscuro.
|
|
186
|
+
- **`release:check`** comprueba que `npm pack` no incluye nada nativo, que los tokens
|
|
187
|
+
nativos están al día y la paridad; con `--with-native` compila y prueba Swift (macOS e
|
|
188
|
+
iOS) y Gradle.
|
|
189
|
+
- Documentación en `native/README.md` y en `CLAUDE.md` § «Nativo (iOS y Android)».
|
|
190
|
+
|
|
191
|
+
### Nativo — componentes de SwiftUI (iOS 17 y macOS 14)
|
|
192
|
+
|
|
193
|
+
> Los primeros componentes nativos, los que pide Homenize. Mismos nombres de props y casos que React, solo tokens,
|
|
194
|
+
> claro y oscuro con colores dinámicos, tipo dinámico, VoiceOver, zona táctil de 44 pt, puntero y foco de teclado en
|
|
195
|
+
> macOS y «reducir movimiento». Detalle y ejemplos en `native/README.md` § «Componentes de SwiftUI». Compose recibirá los
|
|
196
|
+
> mismos componentes en la tarea siguiente.
|
|
197
|
+
|
|
198
|
+
- **15 componentes** portados, cada uno con su ficha de paridad (`native/parity/components/`), capturas en claro y
|
|
199
|
+
oscuro (iOS y macOS), `#Preview` con todas las variantes y la pareja con su story de Storybook
|
|
200
|
+
(`native/apple/Comparisons/<Componente>/`): `Button` (como `ButtonStyle` + `BrandButton`), `Heading`/`Paragraph`/`Text`,
|
|
201
|
+
`Icon`, `InputField`, `NumberInputField`, `SelectField`, `SwitcherField`, `ToggleGroup`, `ThemeSwitcher`, `List` +
|
|
202
|
+
`ListItem`, `Tag`, `EmptyState`, `Skeleton`, `Sheet` (`.sheet` nativo), `ConfirmDialog` y `Toast`/`Toaster`
|
|
203
|
+
(`ToastCenter` + `toastHost`).
|
|
204
|
+
- **Tokens de componente para Swift**: `pnpm build:tokens` genera también `BrandComponentTokens.swift` (un enum por
|
|
205
|
+
grupo de `NATIVE_COMPONENT_GROUPS`, con los `surface-dark-*` como `Color` dinámico, también los heredados por
|
|
206
|
+
referencia). Una medida con par oscuro sale como `BrandSchemeValue`. El CSS puro (`solid`, `vh`, `min()`…) se omite.
|
|
207
|
+
- **`BrandIcon`**: los mismos trazos que el `Icon` de React (77 iconos, retícula de 24), generados desde `Icon.tsx` con
|
|
208
|
+
`pnpm build:native-icons` (incluido en `build:all`).
|
|
209
|
+
- **Paridad**: las fichas admiten `differences` (lo que se queda a propósito) y `reactOnlyValues` (`block: boolean |
|
|
210
|
+
'mobile'`), y `native:parity` entiende uniones numéricas (`Heading level: 1 | … | 6`).
|
|
211
|
+
- **Ayudantes**: `brandFont`/`brandLinedFont` (tipo dinámico e interlineado de CSS), `brandHitTarget` (44 pt),
|
|
212
|
+
`BrandControlSize` y `.brandControlSize(_:)`, `assertBrandSnapshots` (capturas de macOS en sRGB),
|
|
213
|
+
`native/apple/scripts/capture-story.mjs` y `pair-comparison.sh` (parejas React ↔ SwiftUI a la misma escala).
|
|
214
|
+
- Los ejemplos del README compilan (`ReadmeExamples.swift`).
|
|
215
|
+
|
|
10
216
|
## [49.18.0] — 2026-10-03
|
|
11
217
|
|
|
12
218
|
> **Minor.** `RecoveryCodes` deja pasar el aviso de portapapeles roto, y `react-hook-form`
|
package/dist/_shared/tokens.js
CHANGED
|
@@ -316,6 +316,40 @@ var e = {
|
|
|
316
316
|
"--async-select-empty-font-family": "\"Google Sans Flex\", system-ui, sans-serif",
|
|
317
317
|
"--async-select-empty-font-size": "1rem",
|
|
318
318
|
"--async-select-empty-color": "#4a4a4a",
|
|
319
|
+
"--autocomplete-bg": "#ffffff",
|
|
320
|
+
"--autocomplete-border-color": "#111e30",
|
|
321
|
+
"--autocomplete-border-radius": "0px",
|
|
322
|
+
"--autocomplete-border-style": "solid",
|
|
323
|
+
"--autocomplete-border-width": "1px",
|
|
324
|
+
"--autocomplete-disabled-cursor": "not-allowed",
|
|
325
|
+
"--autocomplete-color": "#111e30",
|
|
326
|
+
"--autocomplete-content-bg": "#ffffff",
|
|
327
|
+
"--autocomplete-content-border-color": "#111e30",
|
|
328
|
+
"--autocomplete-content-gap": "0.5rem",
|
|
329
|
+
"--autocomplete-content-z-index": "400",
|
|
330
|
+
"--autocomplete-content-max-height": "var(--available-height)",
|
|
331
|
+
"--autocomplete-error-border-color": "#b30000",
|
|
332
|
+
"--autocomplete-focus-ring-color": "#111e30",
|
|
333
|
+
"--autocomplete-focus-ring-width": "2px",
|
|
334
|
+
"--autocomplete-font-family": "\"Google Sans Flex\", system-ui, sans-serif",
|
|
335
|
+
"--autocomplete-font-size": "1rem",
|
|
336
|
+
"--autocomplete-font-weight": "300",
|
|
337
|
+
"--autocomplete-height": "2.5rem",
|
|
338
|
+
"--autocomplete-item-highlighted-bg": "#111e30",
|
|
339
|
+
"--autocomplete-item-highlighted-color": "#ffffff",
|
|
340
|
+
"--autocomplete-item-padding-block": "0.5rem",
|
|
341
|
+
"--autocomplete-lg-font-size": "1.25rem",
|
|
342
|
+
"--autocomplete-lg-height": "3rem",
|
|
343
|
+
"--autocomplete-lg-item-padding-block": "0.75rem",
|
|
344
|
+
"--autocomplete-lg-padding-inline": "1.5rem",
|
|
345
|
+
"--autocomplete-line-height": "1",
|
|
346
|
+
"--autocomplete-padding-inline": "1rem",
|
|
347
|
+
"--autocomplete-placeholder-color": "#4a4a4a",
|
|
348
|
+
"--autocomplete-sm-font-size": "0.875rem",
|
|
349
|
+
"--autocomplete-sm-height": "2rem",
|
|
350
|
+
"--autocomplete-sm-item-padding-block": "0.25rem",
|
|
351
|
+
"--autocomplete-sm-padding-inline": "0.5rem",
|
|
352
|
+
"--autocomplete-item-selected-font-weight": "500",
|
|
319
353
|
"--avatar-size-sm": "2rem",
|
|
320
354
|
"--avatar-size-md": "2.5rem",
|
|
321
355
|
"--avatar-size-lg": "3rem",
|
|
@@ -1284,6 +1318,11 @@ var e = {
|
|
|
1284
1318
|
"--number-input-lg-font-size": "1.25rem",
|
|
1285
1319
|
"--number-input-lg-height": "3rem",
|
|
1286
1320
|
"--number-input-lg-padding-inline": "1.5rem",
|
|
1321
|
+
"--number-input-compact-height": "2rem",
|
|
1322
|
+
"--number-input-compact-font-size": "0.875rem",
|
|
1323
|
+
"--number-input-compact-btn-width": "1.5rem",
|
|
1324
|
+
"--number-input-compact-field-width": "2.5rem",
|
|
1325
|
+
"--number-input-compact-padding-inline": "0.25rem",
|
|
1287
1326
|
"--org-switcher-trigger-padding-block": "0.5rem",
|
|
1288
1327
|
"--org-switcher-trigger-padding-inline": "0.75rem",
|
|
1289
1328
|
"--org-switcher-trigger-gap": "0.75rem",
|
|
@@ -1835,6 +1874,8 @@ var e = {
|
|
|
1835
1874
|
"--text-inline-muted-color": "#4a4a4a",
|
|
1836
1875
|
"--text-inline-destructive-color": "#b30000",
|
|
1837
1876
|
"--text-inline-success-color": "#006616",
|
|
1877
|
+
"--text-inline-strikethrough-color": "#4a4a4a",
|
|
1878
|
+
"--text-inline-strikethrough-thickness": "1px",
|
|
1838
1879
|
"--text-font-family": "\"Google Sans Flex\", system-ui, sans-serif",
|
|
1839
1880
|
"--text-font-size": "1rem",
|
|
1840
1881
|
"--text-font-weight": "300",
|
|
@@ -1905,6 +1946,13 @@ var e = {
|
|
|
1905
1946
|
"--text-list-letter-spacing": "0em",
|
|
1906
1947
|
"--text-list-gap": "0.5rem",
|
|
1907
1948
|
"--text-list-padding-inline-start": "2rem",
|
|
1949
|
+
"--text-list-item-gap": "0.75rem",
|
|
1950
|
+
"--text-list-secondary-font-size": "0.875rem",
|
|
1951
|
+
"--text-list-secondary-line-height": "1.65",
|
|
1952
|
+
"--text-list-secondary-color": "#4a4a4a",
|
|
1953
|
+
"--text-list-separator-color": "#111e30",
|
|
1954
|
+
"--text-list-separator-thickness": "1px",
|
|
1955
|
+
"--text-list-separator-spacing": "0.5rem",
|
|
1908
1956
|
"--textarea-border-radius": "0px",
|
|
1909
1957
|
"--textarea-border-width": "1px",
|
|
1910
1958
|
"--textarea-border-style": "solid",
|
|
@@ -2118,6 +2166,17 @@ var e = {
|
|
|
2118
2166
|
"--async-select-field-helper-font-weight": "300",
|
|
2119
2167
|
"--async-select-field-helper-line-height": "1.5",
|
|
2120
2168
|
"--async-select-field-helper-color": "#111e30",
|
|
2169
|
+
"--autocomplete-field-gap": "0.5rem",
|
|
2170
|
+
"--autocomplete-field-error-font-family": "\"Google Sans Flex\", system-ui, sans-serif",
|
|
2171
|
+
"--autocomplete-field-error-font-size": "0.875rem",
|
|
2172
|
+
"--autocomplete-field-error-font-weight": "300",
|
|
2173
|
+
"--autocomplete-field-error-line-height": "1.5",
|
|
2174
|
+
"--autocomplete-field-error-color": "#b30000",
|
|
2175
|
+
"--autocomplete-field-helper-font-family": "\"Google Sans Flex\", system-ui, sans-serif",
|
|
2176
|
+
"--autocomplete-field-helper-font-size": "0.875rem",
|
|
2177
|
+
"--autocomplete-field-helper-font-weight": "300",
|
|
2178
|
+
"--autocomplete-field-helper-line-height": "1.5",
|
|
2179
|
+
"--autocomplete-field-helper-color": "#111e30",
|
|
2121
2180
|
"--avatar-upload-gap": "1rem",
|
|
2122
2181
|
"--avatar-upload-body-gap": "0.5rem",
|
|
2123
2182
|
"--avatar-upload-ring-width": "2px",
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { Popover as BasePopover } from '@base-ui/react/popover';
|
|
2
|
+
import './Autocomplete.css';
|
|
3
|
+
export interface AutocompleteOption {
|
|
4
|
+
/** Identifica la sugerencia: es lo que recibe `onSelect` para saber cuál se eligió. */
|
|
5
|
+
value: string;
|
|
6
|
+
/** El texto de la sugerencia: es lo que se escribe en el campo al elegirla. */
|
|
7
|
+
label: string;
|
|
8
|
+
}
|
|
9
|
+
export interface AutocompleteProps {
|
|
10
|
+
/**
|
|
11
|
+
* El texto del campo (controlado). **El valor es el texto escrito**, haya o
|
|
12
|
+
* no una sugerencia que coincida: elegir una sugerencia solo lo rellena.
|
|
13
|
+
*/
|
|
14
|
+
value?: string;
|
|
15
|
+
/** Texto inicial cuando el campo no está controlado. */
|
|
16
|
+
defaultValue?: string;
|
|
17
|
+
/** Se llama con el texto nuevo, tanto al escribir como al elegir una sugerencia. */
|
|
18
|
+
onValueChange?: (value: string) => void;
|
|
19
|
+
/**
|
|
20
|
+
* Se llama, además de `onValueChange`, cuando el usuario **elige** una
|
|
21
|
+
* sugerencia: sirve para saber cuál fue (su `value`) frente a un texto libre.
|
|
22
|
+
*/
|
|
23
|
+
onSelect?: (option: AutocompleteOption) => void;
|
|
24
|
+
/**
|
|
25
|
+
* Sugerencias **síncronas**: una lista fija que el propio control filtra por
|
|
26
|
+
* lo escrito (sin distinguir mayúsculas ni tildes). Es excluyente con
|
|
27
|
+
* `onSearch`.
|
|
28
|
+
*/
|
|
29
|
+
options?: AutocompleteOption[];
|
|
30
|
+
/**
|
|
31
|
+
* Sugerencias **asíncronas** (o síncronas, calculadas por el consumidor): se
|
|
32
|
+
* llama con lo escrito, tras `debounceMs`, y devuelve la lista. Si lanza,
|
|
33
|
+
* simplemente no hay sugerencias: el texto escrito sigue valiendo.
|
|
34
|
+
*/
|
|
35
|
+
onSearch?: (query: string) => AutocompleteOption[] | Promise<AutocompleteOption[]>;
|
|
36
|
+
/**
|
|
37
|
+
* Milisegundos de rebote entre la última tecla y `onSearch`. Default: 200.
|
|
38
|
+
* A 0 se busca en cada tecla. No afecta a `options`.
|
|
39
|
+
*/
|
|
40
|
+
debounceMs?: number;
|
|
41
|
+
/**
|
|
42
|
+
* Caracteres mínimos para sugerir al escribir. Default: 1. La flecha abajo
|
|
43
|
+
* abre la lista aunque no se llegue al mínimo.
|
|
44
|
+
*/
|
|
45
|
+
minChars?: number;
|
|
46
|
+
/** Pista dentro del campo. El control no emite más texto por su cuenta. */
|
|
47
|
+
placeholder?: string;
|
|
48
|
+
disabled?: boolean;
|
|
49
|
+
readOnly?: boolean;
|
|
50
|
+
size?: 'sm' | 'md' | 'lg';
|
|
51
|
+
id?: string;
|
|
52
|
+
/** Nombre del campo en el formulario: el propio `<input>` lleva el texto. */
|
|
53
|
+
name?: string;
|
|
54
|
+
/** Marca el estado de error: aplica la clase `autocomplete--error` y `aria-invalid`. */
|
|
55
|
+
error?: boolean;
|
|
56
|
+
/** Campo obligatorio: `required` nativo, porque lo que se envía es el propio texto. */
|
|
57
|
+
required?: boolean;
|
|
58
|
+
maxLength?: number;
|
|
59
|
+
/** Se llama al salir del control (react-hook-form lo usa para validar). */
|
|
60
|
+
onBlur?: React.FocusEventHandler<HTMLInputElement>;
|
|
61
|
+
/** Se añade DESPUÉS de las clases propias del componente. */
|
|
62
|
+
className?: string;
|
|
63
|
+
/**
|
|
64
|
+
* Nombre accesible cuando el control va suelto, y de la lista de
|
|
65
|
+
* sugerencias. En un campo lo nombra la etiqueta (`htmlFor`), que este
|
|
66
|
+
* atributo pisaría: no lo pongas ahí.
|
|
67
|
+
*/
|
|
68
|
+
'aria-label'?: string;
|
|
69
|
+
'aria-describedby'?: string;
|
|
70
|
+
/**
|
|
71
|
+
* Nodo DOM donde montar el portal de la lista (reenviado a Base UI
|
|
72
|
+
* `Portal.container`). Mismo contrato que `AsyncSelect`: por defecto, el nodo
|
|
73
|
+
* de la superficie que llegue por contexto, o `document.body`.
|
|
74
|
+
*/
|
|
75
|
+
container?: React.ComponentPropsWithoutRef<typeof BasePopover.Portal>['container'];
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Campo de texto con sugerencias. A diferencia de `AsyncSelect`, **no obliga a
|
|
79
|
+
* elegir**: el valor es el texto escrito y una sugerencia es solo una forma de
|
|
80
|
+
* escribirlo más deprisa. Patrón ARIA de combobox con lista (`aria-activedescendant`):
|
|
81
|
+
* el foco no sale nunca del `<input>`. El `ref` va a ese `<input>`.
|
|
82
|
+
*/
|
|
83
|
+
export declare const Autocomplete: import("react").ForwardRefExoticComponent<AutocompleteProps & import("react").RefAttributes<HTMLInputElement>>;
|
|
@@ -8,10 +8,30 @@ export interface ListItemProps extends React.ComponentPropsWithoutRef<'li'> {
|
|
|
8
8
|
*/
|
|
9
9
|
as?: React.ElementType;
|
|
10
10
|
children?: React.ReactNode;
|
|
11
|
+
/**
|
|
12
|
+
* Accesorio al principio de la fila: un icono, un avatar. Pareja de
|
|
13
|
+
* `trailing`; no se encoge ni parte línea y se centra con el bloque de texto.
|
|
14
|
+
*/
|
|
15
|
+
leading?: React.ReactNode;
|
|
16
|
+
/**
|
|
17
|
+
* Línea menor bajo el contenido, atenuada y un peldaño por debajo del cuerpo
|
|
18
|
+
* (`text.paragraph.small`): la descripción de una fila de datos o de ajustes.
|
|
19
|
+
*/
|
|
20
|
+
secondary?: React.ReactNode;
|
|
21
|
+
/**
|
|
22
|
+
* Accesorio al final de la fila, alineado a la derecha: un icono, un valor,
|
|
23
|
+
* un interruptor. No se encoge ni parte línea.
|
|
24
|
+
*/
|
|
25
|
+
trailing?: React.ReactNode;
|
|
11
26
|
}
|
|
12
27
|
export interface ListProps extends React.ComponentPropsWithoutRef<'ul'> {
|
|
13
28
|
/** Tipo de lista: con viñetas, numerada o sin decoración. */
|
|
14
29
|
type?: ListType;
|
|
30
|
+
/**
|
|
31
|
+
* Dibuja una línea entre filas con los tokens `separator.*`, con su aire a
|
|
32
|
+
* cada lado en lugar del `text.list.gap`. Default `false`: sin línea.
|
|
33
|
+
*/
|
|
34
|
+
showSeparators?: boolean;
|
|
15
35
|
children: React.ReactNode;
|
|
16
36
|
}
|
|
17
37
|
/**
|
|
@@ -10,9 +10,20 @@ export interface NumberInputMessages {
|
|
|
10
10
|
/** Nombre accesible del botón que suma un paso. */
|
|
11
11
|
increment: string;
|
|
12
12
|
}
|
|
13
|
+
/**
|
|
14
|
+
* Cuándo avisa `onChange` de lo escrito a mano: con cada tecla (`'change'`, lo de
|
|
15
|
+
* siempre) o una sola vez al confirmar (`'blur'`). Los botones − y + avisan al
|
|
16
|
+
* momento en los dos modos.
|
|
17
|
+
*/
|
|
18
|
+
export type NumberInputCommitMode = 'change' | 'blur';
|
|
13
19
|
export interface NumberInputProps extends Omit<ComponentPropsWithoutRef<'input'>, 'size' | 'type' | 'value' | 'defaultValue' | 'onChange'> {
|
|
14
|
-
|
|
15
|
-
|
|
20
|
+
/**
|
|
21
|
+
* Valor controlado. `null` es «sin valor»: el campo se muestra vacío (y el
|
|
22
|
+
* `placeholder` se ve). Sigue siendo controlado; `undefined` es no controlado.
|
|
23
|
+
*/
|
|
24
|
+
value?: number | null;
|
|
25
|
+
/** Valor inicial no controlado (default `0`). `null` arranca vacío. */
|
|
26
|
+
defaultValue?: number | null;
|
|
16
27
|
min?: number;
|
|
17
28
|
max?: number;
|
|
18
29
|
step?: number;
|
|
@@ -20,6 +31,19 @@ export interface NumberInputProps extends Omit<ComponentPropsWithoutRef<'input'>
|
|
|
20
31
|
disabled?: boolean;
|
|
21
32
|
readOnly?: boolean;
|
|
22
33
|
size?: 'sm' | 'md' | 'lg';
|
|
34
|
+
/**
|
|
35
|
+
* Variante para filas de lista (`trailing` de `ListItem`): botones y cifra
|
|
36
|
+
* justos, del ancho de 2–3 dígitos, sin estirarse. Es una variante de la
|
|
37
|
+
* talla `sm`, no una talla más: manda sobre `size`.
|
|
38
|
+
*/
|
|
39
|
+
compact?: boolean;
|
|
40
|
+
/**
|
|
41
|
+
* Cuándo se avisa de lo escrito a mano. `'change'` (por defecto): con cada
|
|
42
|
+
* tecla. `'blur'`: una sola vez al salir del campo o al pulsar Enter; Escape
|
|
43
|
+
* descarta lo escrito y vuelve al último valor. Con `onEmpty`, dejar el campo
|
|
44
|
+
* vacío se avisa igual, al confirmar. − y + avisan al momento en los dos modos.
|
|
45
|
+
*/
|
|
46
|
+
commitMode?: NumberInputCommitMode;
|
|
23
47
|
error?: boolean;
|
|
24
48
|
id?: string;
|
|
25
49
|
name?: string;
|
|
@@ -40,6 +64,13 @@ export interface NumberInputProps extends Omit<ComponentPropsWithoutRef<'input'>
|
|
|
40
64
|
*/
|
|
41
65
|
incrementLabel?: string;
|
|
42
66
|
onChange?: (value: number) => void;
|
|
67
|
+
/**
|
|
68
|
+
* Se llama cuando quien teclea deja el campo vacío. Sin ella el campo se
|
|
69
|
+
* comporta como siempre (vaciar no emite nada y al salir recupera el último
|
|
70
|
+
* número); con ella, vaciar **es** un valor: el campo pasa a «sin valor» y
|
|
71
|
+
* esta función avisa de ello.
|
|
72
|
+
*/
|
|
73
|
+
onEmpty?: () => void;
|
|
43
74
|
onBlur?: React.FocusEventHandler<HTMLInputElement>;
|
|
44
75
|
onFocus?: React.FocusEventHandler<HTMLInputElement>;
|
|
45
76
|
}
|
|
@@ -5,13 +5,21 @@ export interface TextProps extends React.ComponentPropsWithoutRef<'span'> {
|
|
|
5
5
|
* `span` no añade significado, `em` marca énfasis de lectura (cambia cómo se
|
|
6
6
|
* dice la frase) y `strong` marca importancia. No es una prop de estilo.
|
|
7
7
|
*/
|
|
8
|
-
as?: 'span' | 'em' | 'strong';
|
|
8
|
+
as?: 'span' | 'em' | 'strong' | 'del' | 's';
|
|
9
9
|
/**
|
|
10
10
|
* Intención del fragmento. `destructive` es la palabra que dice que algo se
|
|
11
11
|
* pierde («esta acción **borra** el curso»); `success`, la que dice que salió
|
|
12
12
|
* bien; `muted`, una aclaración secundaria.
|
|
13
13
|
*/
|
|
14
14
|
tone?: 'default' | 'muted' | 'destructive' | 'success';
|
|
15
|
+
/**
|
|
16
|
+
* Tacha el fragmento con una línea y lo atenúa (tinta secundaria): lo que ya
|
|
17
|
+
* está hecho, como un producto que ya está en el carrito. Es **solo
|
|
18
|
+
* aspecto**: no dice nada al lector de pantalla. Si el tachado significa algo
|
|
19
|
+
* («este precio ya no vale»), usa `as="del"` o `as="s"`, que además lo tachan.
|
|
20
|
+
* Con un `tone` manda el color del tono.
|
|
21
|
+
*/
|
|
22
|
+
strikethrough?: boolean;
|
|
15
23
|
/**
|
|
16
24
|
* Idioma de **este fragmento**, cuando no es el de la página: una cita, un
|
|
17
25
|
* término sin traducir, un segmento de traducción. Marca el idioma para el
|
|
@@ -25,11 +33,13 @@ export interface TextProps extends React.ComponentPropsWithoutRef<'span'> {
|
|
|
25
33
|
}
|
|
26
34
|
/**
|
|
27
35
|
* Texto **en línea**: un trozo de una frase que hay que marcar sin salirse de
|
|
28
|
-
* ella.
|
|
36
|
+
* ella. Tres usos, y ningún otro:
|
|
29
37
|
*
|
|
30
38
|
* - **Otro idioma** (`lang`): el fragmento se anuncia con la voz correcta y se
|
|
31
39
|
* corta según sus reglas. Antes esto era un `<span lang>` a mano en cada
|
|
32
40
|
* producto.
|
|
41
|
+
* - **Tachado** (`strikethrough`, o `as="del"`/`as="s"`): lo hecho o lo que ya
|
|
42
|
+
* no vale, en tinta atenuada.
|
|
33
43
|
* - **Intención** (`tone`): énfasis con carga —destructiva o de logro— en tinta
|
|
34
44
|
* de feedback. Es color de texto sobre la superficie, **nunca un relleno**:
|
|
35
45
|
* una palabra resaltada dentro de un párrafo no lleva fondo.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import './AutocompleteField.css';
|
|
2
|
+
import { Autocomplete } from '../../atoms/Autocomplete/Autocomplete';
|
|
3
|
+
import type { AutocompleteOption } from '../../atoms/Autocomplete/Autocomplete';
|
|
4
|
+
export type { AutocompleteOption };
|
|
5
|
+
export interface AutocompleteFieldProps {
|
|
6
|
+
/** `id` del control. Si no se pasa, se genera con `useId`. */
|
|
7
|
+
id?: string;
|
|
8
|
+
label: string;
|
|
9
|
+
/**
|
|
10
|
+
* Oculta la etiqueta a la vista (sigue leyéndola el lector de pantalla).
|
|
11
|
+
* Por defecto `false`: la etiqueta se ve.
|
|
12
|
+
* Sin valor, lo decide quien lo envuelva: dentro de un `FieldRow` que no
|
|
13
|
+
* es la primera de la lista, la etiqueta se oculta sola.
|
|
14
|
+
*/
|
|
15
|
+
labelHidden?: boolean;
|
|
16
|
+
/** El texto del campo (controlado). El valor es lo escrito, coincida o no con una sugerencia. */
|
|
17
|
+
value?: string;
|
|
18
|
+
/** Texto inicial cuando el campo no está controlado. */
|
|
19
|
+
defaultValue?: string;
|
|
20
|
+
/** Se llama con el texto nuevo, al escribir y al elegir una sugerencia. */
|
|
21
|
+
onValueChange?: (value: string) => void;
|
|
22
|
+
/** Se llama, además, cuando se elige una sugerencia: para saber cuál fue. */
|
|
23
|
+
onSelect?: (option: AutocompleteOption) => void;
|
|
24
|
+
/** Sugerencias síncronas: lista fija que el control filtra por lo escrito. Excluyente con `onSearch`. */
|
|
25
|
+
options?: AutocompleteOption[];
|
|
26
|
+
/** Sugerencias asíncronas (o calculadas por el consumidor), con rebote. */
|
|
27
|
+
onSearch?: (query: string) => AutocompleteOption[] | Promise<AutocompleteOption[]>;
|
|
28
|
+
/** Milisegundos de rebote antes de llamar a `onSearch`. Default: 200. */
|
|
29
|
+
debounceMs?: number;
|
|
30
|
+
/** Caracteres mínimos para sugerir al escribir. Default: 1. */
|
|
31
|
+
minChars?: number;
|
|
32
|
+
placeholder?: string;
|
|
33
|
+
/** Nombre del campo en el formulario: el propio `<input>` lleva el texto. */
|
|
34
|
+
name?: string;
|
|
35
|
+
disabled?: boolean;
|
|
36
|
+
readOnly?: boolean;
|
|
37
|
+
/** Campo obligatorio (`required` nativo). */
|
|
38
|
+
required?: boolean;
|
|
39
|
+
maxLength?: number;
|
|
40
|
+
/** Marca el control en error sin mensaje. Un `errorMessage` ya lo implica. */
|
|
41
|
+
error?: boolean;
|
|
42
|
+
/** Mensaje de error: se anuncia (`role="alert"`) y pone el control en error. */
|
|
43
|
+
errorMessage?: string;
|
|
44
|
+
/** Texto de ayuda, enlazado por `aria-describedby`. */
|
|
45
|
+
helperText?: string;
|
|
46
|
+
size?: 'sm' | 'md' | 'lg';
|
|
47
|
+
/** Se añade DESPUÉS de las clases propias (el consumidor añade, no sustituye). */
|
|
48
|
+
className?: string;
|
|
49
|
+
/** Nodo DOM donde montar el portal de la lista (ver `Autocomplete`). */
|
|
50
|
+
container?: React.ComponentProps<typeof Autocomplete>['container'];
|
|
51
|
+
onBlur?: React.FocusEventHandler<HTMLInputElement>;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* El `Autocomplete` como campo de formulario: etiqueta, ayuda y error. El
|
|
55
|
+
* `ref` va al `<input>`; el `className`, al contenedor.
|
|
56
|
+
*/
|
|
57
|
+
export declare const AutocompleteField: import("react").ForwardRefExoticComponent<AutocompleteFieldProps & import("react").RefAttributes<HTMLInputElement>>;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type ComponentPropsWithoutRef } from 'react';
|
|
2
2
|
import './NumberInputField.css';
|
|
3
|
+
import { type NumberInputCommitMode } from '../../atoms/NumberInput/NumberInput';
|
|
3
4
|
export interface NumberInputFieldProps extends Omit<ComponentPropsWithoutRef<'input'>, 'size' | 'type' | 'value' | 'defaultValue' | 'onChange'> {
|
|
4
5
|
/** `id` del control. Si no se pasa, se genera con `useId`. */
|
|
5
6
|
id?: string;
|
|
@@ -11,8 +12,9 @@ export interface NumberInputFieldProps extends Omit<ComponentPropsWithoutRef<'in
|
|
|
11
12
|
* es la primera de la lista, la etiqueta se oculta sola.
|
|
12
13
|
*/
|
|
13
14
|
labelHidden?: boolean;
|
|
14
|
-
|
|
15
|
-
|
|
15
|
+
/** `null` es «sin valor»: el campo se muestra vacío. Ver `NumberInput`. */
|
|
16
|
+
value?: number | null;
|
|
17
|
+
defaultValue?: number | null;
|
|
16
18
|
min?: number;
|
|
17
19
|
max?: number;
|
|
18
20
|
step?: number;
|
|
@@ -25,8 +27,18 @@ export interface NumberInputFieldProps extends Omit<ComponentPropsWithoutRef<'in
|
|
|
25
27
|
/** Texto de ayuda, enlazado por `aria-describedby`. */
|
|
26
28
|
helperText?: string;
|
|
27
29
|
size?: 'sm' | 'md' | 'lg';
|
|
30
|
+
/** Variante para filas de lista: botones y cifra justos. Ver `NumberInput`. */
|
|
31
|
+
compact?: boolean;
|
|
32
|
+
/** Cuándo se avisa de lo escrito a mano: con cada tecla (`'change'`) o al salir/Enter (`'blur'`). Ver `NumberInput`. */
|
|
33
|
+
commitMode?: NumberInputCommitMode;
|
|
34
|
+
/** aria-label del botón que resta. Sin default: sale de `numberInput.decrement` del `BrandMessagesProvider`. */
|
|
35
|
+
decrementLabel?: string;
|
|
36
|
+
/** aria-label del botón que suma. Sin default: sale de `numberInput.increment` del `BrandMessagesProvider`. */
|
|
37
|
+
incrementLabel?: string;
|
|
28
38
|
/** Recibe el valor ya normalizado, no el evento. */
|
|
29
39
|
onChange?: (value: number) => void;
|
|
40
|
+
/** Quien teclea dejó el campo vacío; con ella, vaciar es un valor (`null`). Ver `NumberInput`. */
|
|
41
|
+
onEmpty?: () => void;
|
|
30
42
|
/** Se añade DESPUÉS de las clases propias (el consumidor añade, no sustituye). */
|
|
31
43
|
className?: string;
|
|
32
44
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.autocomplete-field{gap:var(--autocomplete-field-gap);flex-direction:column;display:flex}.autocomplete-field__helper{font-family:var(--autocomplete-field-helper-font-family);font-size:var(--autocomplete-field-helper-font-size);font-weight:var(--autocomplete-field-helper-font-weight);line-height:var(--autocomplete-field-helper-line-height);color:var(--autocomplete-field-helper-color)}
|