@aurea-uds/native 0.31.0 → 0.32.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/dist/actions.d.ts CHANGED
@@ -87,11 +87,25 @@ export declare function IconButton(props: IconButtonProps): React.JSX.Element;
87
87
  /**
88
88
  * Quanto a ÁREA DE TOQUE do `IconButton` passa do círculo desenhado, de cada lado — R-24
89
89
  * (08/10/2026). O alvo é o maior entre o desenho e o `targetMin` (44), centrado; o círculo de
90
- * `md` tem 36, então sobram 4 de cada lado. O `Badge` preso usa isto para se prender ao CÍRCULO,
90
+ * `md` tinha 36, e sobravam 4 de cada lado. O `Badge` preso usa isto para se prender ao CÍRCULO,
91
91
  * e não ao canto da área de toque — medido: preso à área de toque, o selo ficava 4 para fora.
92
+ * 0.32.0: com a escala de toque o `md` tem 48 e a folga é 0; sobra no `sm` (40: 2) e no `xs`.
92
93
  */
93
94
  export declare function folgaDoToque(t: AureaTokens, size?: AureaButtonSize): number;
94
95
  /** Fechado: sem `appearance` e sem `tone`, porque a cor é a do glifo. */
95
96
  export interface ThemeToggleProps extends Omit<IconButtonProps, "name" | "label" | "onPress" | "appearance" | "tone" | "share"> {
96
97
  }
97
98
  export declare function ThemeToggle(props: ThemeToggleProps): React.JSX.Element;
99
+ export interface CloseButtonProps {
100
+ /** O nome que o leitor de tela anuncia. Padrão: a frase `close` ("Close"/"Fechar"). */
101
+ label?: string;
102
+ onPress?: PressableProps["onPress"];
103
+ disabled?: boolean;
104
+ /** Registro local de ícones, quando não há um em contexto. */
105
+ icons?: AureaIconRegistry;
106
+ /** Troca o X. */
107
+ children?: React.ReactNode;
108
+ testID?: string;
109
+ }
110
+ /** O botão de fechar: círculo neutro com o X, que já se chama "Close"/"Fechar". */
111
+ export declare function CloseButton({ label, onPress, disabled, icons, children, testID }: CloseButtonProps): React.JSX.Element;
package/dist/actions.js CHANGED
@@ -26,17 +26,22 @@ import { canto, criarFolha, REACAO_AO_TOQUE, estadoAcessivel } from "./estilos.j
26
26
  import { Icon } from "./icon.js";
27
27
  import { Text } from "./text.js";
28
28
  import { AvisosContext, useAureaStrings, useAureaTheme, useAureaTokens, useSobreAMarca } from "./theme.js";
29
- const ALTURA = {
30
- xs: "controlHXs", sm: "controlHSm", md: "controlHMd", lg: "controlHLg", xl: "controlHXl",
29
+ const MEDIDA = {
30
+ xs: (s) => ({ altura: s.controlHSm, recheio: s.space3, vao: s.space1 + s.space05 }),
31
+ sm: (s) => ({ altura: s.touchControlHSm, recheio: s.space3 + s.space05, vao: s.space1 + s.space05 }),
32
+ md: (s) => ({ altura: s.touchControlHMd, recheio: s.space4, vao: s.space2 }),
33
+ lg: (s) => ({ altura: s.touchControlHLg, recheio: s.space5, vao: s.space2 + s.space05 }),
34
+ xl: (s) => ({ altura: s.touchControlHLg + s.space2, recheio: s.space6, vao: s.space2 + s.space05 }),
31
35
  };
32
- // Os mesmos números do `.btn-*` do core, medidos lá — não reinventados aqui.
33
- const PADDING = { xs: 10, sm: 12, md: 15, lg: 20, xl: 26 };
34
- const GAP = { xs: 6, sm: 8, md: 8, lg: 8, xl: 10 };
36
+ // O degrau do `Text` do nativo, que sobe um em relação à web (ADR-0050): `xs` é 14, `base` é 16 e
37
+ // `lg` é 18.
35
38
  const FONTE = {
36
- xs: "xs", sm: "xs", md: "sm", lg: "base", xl: "base",
39
+ xs: "xs", sm: "xs", md: "base", lg: "lg", xl: "lg",
37
40
  };
41
+ // O ícone acompanha a altura nova: 16 até o `sm` (40), 20 no `md` (48), 24 do `lg` (56) para cima.
42
+ // Era 16 até o `md` e 20 no `lg` e no `xl`, quando o `md` tinha 36.
38
43
  const ICONE = {
39
- xs: "sm", sm: "sm", md: "sm", lg: "md", xl: "md",
44
+ xs: "sm", sm: "sm", md: "md", lg: "lg", xl: "lg",
40
45
  };
41
46
  /** Par (fundo, texto, borda) por tom, para cada aparência. Tudo de token, nada cru. */
42
47
  function pintar(t, tone) {
@@ -121,6 +126,8 @@ const folha = criarFolha((t) => ({
121
126
  // pinta fundo, borda e raio é a `caixa` interna, com a altura do token. O botão continua com
122
127
  // 36 dp de desenho e passa a ter 44 de alvo — e o leitor de tela enxerga os 44, porque o
123
128
  // elemento acessível é o `Pressable`, não um retângulo invisível ao lado dele.
129
+ // 0.32.0: com a escala de toque, o `md` (48) e o `lg` (56) já passam do alvo sozinhos; o `sm`
130
+ // (40) e o `xs` (32) continuam ganhando os 44 por aqui.
124
131
  // E2 (25/09/2026): SEM `alignSelf`. O botão obedece o pai, como na referência (a folha do botão dela
125
132
  // não fixa alinhamento) e como o `.btn` da web num `.stack`. Era `alignSelf: "flex-start"`, e ele
126
133
  // vencia o `alignItems: "center"` do pai: o botão do `EmptyState` ficava à esquerda com o resto
@@ -209,17 +216,18 @@ function CorpoDoBotao({ children, appearance = "solid", tone = "neutral", size =
209
216
  const marca = useSobreAMarca();
210
217
  const cor = pintarSobreAMarca(pintar(t, tone), marca, tone, appearance);
211
218
  const corDaBorda = marca?.tinta ?? t.color.border;
219
+ const medida = MEDIDA[size](t.size);
212
220
  const caixa = React.useMemo(() => ({
213
- height: t.size[ALTURA[size]],
214
- paddingHorizontal: PADDING[size],
215
- gap: GAP[size],
221
+ height: medida.altura,
222
+ paddingHorizontal: medida.recheio,
223
+ gap: medida.vao,
216
224
  backgroundColor: appearance === "solid" ? cor.solido : "transparent",
217
225
  borderColor: cor.contorno ?? (appearance === "outline" ? corDaBorda : "transparent"),
218
226
  ...(fullWidth ? { flex: 1 } : null),
219
227
  // A regra do botão-link da referência: `height: auto; padding: 0`, e a do botão dela já
220
228
  // tem `border-width: 0`. A borda transparente de 1 empurraria o texto 1 para dentro.
221
229
  ...(semRecuo ? { height: undefined, paddingHorizontal: 0, borderWidth: 0 } : null),
222
- }), [t, size, appearance, cor.solido, cor.contorno, corDaBorda, fullWidth, semRecuo]);
230
+ }), [medida.altura, medida.recheio, medida.vao, appearance, cor.solido, cor.contorno, corDaBorda, fullWidth, semRecuo]);
223
231
  const corDoTexto = appearance === "solid" ? cor.texto : cor.sobre;
224
232
  return (_jsx(Pressable, { disabled: disabled, onPress: aoTocar, accessibilityRole: "button", accessibilityLabel: accessibilityLabel, ...estadoAcessivel({ disabled: !!disabled, pressed }), style: ({ pressed: tocando }) => [
225
233
  s.alvo, fullWidth && s.alvoLargura,
@@ -231,8 +239,8 @@ function CorpoDoBotao({ children, appearance = "solid", tone = "neutral", size =
231
239
  // Atkinson Hyperlegible (ADR-0053) sobe 0,984 e desce 0,316, o mesmo 1,3 (medido na
232
240
  // tabela `hhea` dos dois arquivos, 01/10/2026): no Android o RN
233
241
  // corta o que passa da linha, e a perna do g, do p e do ç sumia. Cabe em todo tamanho e
234
- // densidade (medido): `xs`/`sm` têm linha de 21 e o menor botão mede 24 (compacto); os
235
- // maiores têm linha de 24 e medem 32 ou mais.
242
+ // densidade (conta da 0.32.0): `xs`/`sm` têm linha de 21 e o menor botão mede 28 (`xs`
243
+ // no compacto); o `md` tem linha de 24 e mede 44 ou mais; `lg`/`xl`, 27 e 52 ou mais.
236
244
  ? _jsx(Text, { size: FONTE[size], weight: 500, leading: "normal", style: { color: corDoTexto }, children: children })
237
245
  : children, trailingIcon ? _jsx(Icon, { name: trailingIcon, size: ICONE[size], color: corDoTexto, icons: icons }) : null, trailing ?? null] }) }));
238
246
  }
@@ -256,11 +264,12 @@ export function IconButton(props) {
256
264
  /**
257
265
  * Quanto a ÁREA DE TOQUE do `IconButton` passa do círculo desenhado, de cada lado — R-24
258
266
  * (08/10/2026). O alvo é o maior entre o desenho e o `targetMin` (44), centrado; o círculo de
259
- * `md` tem 36, então sobram 4 de cada lado. O `Badge` preso usa isto para se prender ao CÍRCULO,
267
+ * `md` tinha 36, e sobravam 4 de cada lado. O `Badge` preso usa isto para se prender ao CÍRCULO,
260
268
  * e não ao canto da área de toque — medido: preso à área de toque, o selo ficava 4 para fora.
269
+ * 0.32.0: com a escala de toque o `md` tem 48 e a folga é 0; sobra no `sm` (40: 2) e no `xs`.
261
270
  */
262
271
  export function folgaDoToque(t, size = "md") {
263
- const lado = t.size[ALTURA[size]];
272
+ const lado = MEDIDA[size](t.size).altura;
264
273
  return Math.max(0, (Math.max(lado, t.size.targetMin) - lado) / 2);
265
274
  }
266
275
  /** O corpo do `IconButton`. A cor própria do ícone (`corDoIcone`) é só do `ThemeToggle`: o
@@ -272,13 +281,14 @@ function BotaoDeIcone({ name, label, appearance = "ghost", tone = "neutral", siz
272
281
  const marca = useSobreAMarca();
273
282
  const cor = pintarSobreAMarca(pintar(t, tone), marca, tone, appearance);
274
283
  const corDaBorda = marca?.tinta ?? t.color.border;
275
- const lado = t.size[ALTURA[size]];
284
+ // O círculo tem a altura do botão do mesmo tamanho (o só-ícone da referência: `aspect-ratio: 1`).
285
+ const lado = MEDIDA[size](t.size).altura;
276
286
  const caixa = React.useMemo(() => ({
277
287
  height: lado, width: lado, paddingHorizontal: 0,
278
288
  ...canto(t.size.radiusControl),
279
289
  backgroundColor: appearance === "solid" ? cor.solido : "transparent",
280
290
  borderColor: cor.contorno ?? (appearance === "outline" ? corDaBorda : "transparent"),
281
- }), [t, lado, size, appearance, cor.solido, cor.contorno, corDaBorda]);
291
+ }), [t, lado, appearance, cor.solido, cor.contorno, corDaBorda]);
282
292
  return (_jsx(Pressable, { disabled: disabled, onPress: aoTocar, accessibilityRole: "button", accessibilityLabel: label, ...estadoAcessivel({ disabled: !!disabled, pressed }), style: ({ pressed: tocando }) => [
283
293
  // Largura FIXA, como o só-ícone da referência: ele nunca estica,
284
294
  // nem numa coluna sem alinhamento. O alvo é o maior entre o desenho e o `targetMin`.
@@ -293,3 +303,16 @@ export function ThemeToggle(props) {
293
303
  const escuro = theme === "dark";
294
304
  return (_jsx(BotaoDeIcone, { ...props, name: escuro ? "sun" : "moon", label: escuro ? s.themeToLight : s.themeToDark, corDoIcone: escuro ? t.color.primary : t.color.foreground, pesoDoIcone: "fill", onPress: toggleTheme }));
295
305
  }
306
+ /** O botão de fechar: círculo neutro com o X, que já se chama "Close"/"Fechar". */
307
+ export function CloseButton({ label, onPress, disabled, icons, children, testID }) {
308
+ const t = useAureaTokens();
309
+ const s = useAureaStrings();
310
+ const lado = t.size.touchControlHSm;
311
+ return (_jsx(Pressable, { disabled: disabled, onPress: onPress, accessibilityRole: "button", accessibilityLabel: label ?? s.close, ...estadoAcessivel({ disabled: !!disabled }), hitSlop: t.size.space3, testID: testID, style: ({ pressed: tocando }) => [
312
+ { width: lado, height: lado, alignItems: "center", justifyContent: "center",
313
+ ...canto(t.size.radiusFull), backgroundColor: t.color.secondary },
314
+ // Encolhe a 93%, como o da referência (o botão comum da Aurea vai a 97%).
315
+ tocando && { opacity: REACAO_AO_TOQUE.pressionado.opacity, transform: [{ scale: 0.93 }] },
316
+ disabled && REACAO_AO_TOQUE.inerte,
317
+ ], children: children ?? _jsx(Icon, { name: "x", weight: "bold", size: "md", icons: icons, color: t.color.mutedForeground }) }));
318
+ }
package/dist/busca.js CHANGED
@@ -72,20 +72,16 @@ import { IconButton } from "./actions.js";
72
72
  import { canto, cantosDeCima, criarFolha, estadoAcessivel } from "./estilos.js";
73
73
  import { Spinner } from "./feedback.js";
74
74
  import { Icon } from "./icon.js";
75
- import { KeyboardAvoiding, useCampo } from "./inputs.js";
75
+ import { KeyboardAvoiding, alturaDoTamanho, indicadorDaEscolhida, respiroDoGatilho, respiroDoTamanho, useCampo, } from "./inputs.js";
76
76
  import { useReduceMotion, driverNativo } from "./movimento.js";
77
77
  import { RecuoDaFolha } from "./screen.js";
78
78
  import { Text } from "./text.js";
79
79
  import { useAureaStrings, useAureaTokens, useTokensDoApp, ForaDaMarca, usePeleSobreAMarca } from "./theme.js";
80
- // As mesmas contas do `inputs.tsx`, e elas são repetidas AQUI de propósito: exportá-las de lá
81
- // tornaria pública uma conta interna do outro módulo, e um `import` de função privada entre
82
- // módulos irmãos é o começo de um acoplamento que ninguém declara. São três linhas.
83
- const alturaDoTamanho = (t, s) => s === "sm" ? t.size.controlHSm : s === "lg" ? t.size.controlHLg : t.size.controlHMd;
80
+ // A altura e o respiro do campo vêm do `inputs.tsx` (0.32.0). Eram repetidos aqui, de propósito,
81
+ // para não tornar pública uma conta interna de lá — mas desde 02/10/2026 o `inputs.tsx` já os
82
+ // exporta ao `FileInput`, e a escala de toque mostrou o custo da cópia: duas contas para mudar
83
+ // juntas. Saem do arquivo, não do pacote.
84
84
  const fonteDoTamanho = (t, s) => s === "sm" ? t.size.textSm : s === "lg" ? t.size.textLg : t.size.textBase; // ADR-0050: um degrau acima da web
85
- // ⚠ O `13` do tamanho `md` NÃO é número inventado: é o `padding-inline` literal do `.input`
86
- // (`aurea.css:685`), e é o mesmo valor que o `inputs.tsx:60` usa. Não há token de 13 — a escala
87
- // tem `space3`=12 e `space4`=16 —, e é por isso que o CSS o escreve cru dos dois lados.
88
- const respiroDoTamanho = (t, s) => s === "sm" ? t.size.space3 : s === "lg" ? t.size.space4 : 13;
89
85
  // ── AS DUAS CONSTANTES DE INTERAÇÃO, e elas NÃO são geometria ────────────────────────────────
90
86
  // Ficam nomeadas e no topo porque número solto no meio do componente é o que o `CLAUDE.md`
91
87
  // passou a proibir em 12/09/2026. **Nenhuma das duas tem linha de `aurea.css` atrás, e não
@@ -175,10 +171,14 @@ const folha = criarFolha((t) => ({
175
171
  // `CLAUDE.md` ("correção local é proibida sem responder quem mais tem esse problema") é o que
176
172
  // traz as duas linhas para cá, e este campo é exatamente "quem mais".
177
173
  campoDeTexto: { flex: 1, minWidth: 0, paddingVertical: 0, textAlignVertical: "center" },
174
+ // 0.32.0: a opção da lista do `Select` (`inputs.tsx`), que é a da referência — 12 em cima e
175
+ // embaixo, 8 dos lados, 8 até o indicador, 48 de toque no mínimo. Era `controlHLg` (40), 16 dos
176
+ // lados e 8 em cima e embaixo. As duas folhas continuam iguais, que é o combinado acima.
178
177
  opcao: {
179
- minHeight: t.size.controlHLg, justifyContent: "center",
180
- paddingHorizontal: t.size.space4, paddingVertical: t.size.space2,
178
+ minHeight: t.size.touchControlHMd, flexDirection: "row", alignItems: "center", gap: t.size.space2,
179
+ paddingHorizontal: t.size.space2, paddingVertical: t.size.space3,
181
180
  },
181
+ textoDaOpcao: { flex: 1, minWidth: 0 },
182
182
  opcaoEscolhida: { backgroundColor: t.color.surface3 ?? t.color.muted },
183
183
  vazio: { paddingHorizontal: t.size.space4, paddingVertical: t.size.space5, alignItems: "center" },
184
184
  carregando: { paddingVertical: t.size.space4, alignItems: "center" },
@@ -304,7 +304,9 @@ export function Combobox({ items, value, onValueChange, onSearchChange, searchDe
304
304
  const nada = !loading && filtrados.length === 0;
305
305
  return (_jsxs(_Fragment, { children: [_jsxs(View, { style: [
306
306
  s.caixa,
307
- { height: alturaDoTamanho(t, tam), paddingHorizontal: respiroDoTamanho(t, tam) },
307
+ // 0.32.0: o gatilho do `Select` — altura de toque, 16 dos lados no `md` e 12 entre o
308
+ // texto e as ações (era 13 e nada).
309
+ { height: alturaDoTamanho(t, tam), paddingHorizontal: respiroDoGatilho(t, tam), gap: t.size.space3 },
308
310
  campo?.invalido && s.invalido,
309
311
  // Sobre a marca a tinta vence inválido — ver `usePeleSobreAMarca`.
310
312
  peleDaMarca,
@@ -324,7 +326,7 @@ export function Combobox({ items, value, onValueChange, onSearchChange, searchDe
324
326
  sf.campoDeTexto,
325
327
  { fontSize: fonteDoTamanho(t, tam), fontFamily: tf.font.ui[400],
326
328
  color: tf.color.foreground },
327
- ] }), texto.length > 0 && (_jsx(IconButton, { name: "x", label: strings.searchClear, appearance: "ghost", size: "sm", onPress: () => digitar(""), testID: testID ? `${testID}-busca-limpar` : undefined }))] }), loading && (_jsx(View, { style: sf.carregando, children: _jsx(Spinner, { label: strings.comboboxLoading }) })), nada && (_jsx(View, { style: sf.vazio, children: typeof empty === "string" || empty == null
329
+ ] }), texto.length > 0 && (_jsx(IconButton, { name: "x", label: strings.searchClear, appearance: "ghost", size: "sm", onPress: () => digitar(""), testID: testID ? `${testID}-busca-limpar` : undefined }))] }), loading && (_jsx(View, { style: sf.carregando, children: _jsx(Spinner, { size: "sm", label: strings.comboboxLoading }) })), nada && (_jsx(View, { style: sf.vazio, children: typeof empty === "string" || empty == null
328
330
  ? _jsx(Text, { size: "sm", tone: "muted", children: empty ?? strings.comboboxEmpty })
329
331
  : empty })), _jsx(FlatList, { data: filtrados, keyExtractor: (i) => i.value, keyboardShouldPersistTaps: "handled", onEndReached: onEndReached, onEndReachedThreshold: ANTECEDENCIA_DE_PAGINA, renderItem: ({ item }) => (_jsxs(Pressable, { disabled: item.disabled, onPress: () => { onValueChange?.(item); fechar(); }, accessibilityRole: "menuitem", ...estadoAcessivel({
330
332
  selected: item.value === value?.value, disabled: !!item.disabled,
@@ -332,7 +334,7 @@ export function Combobox({ items, value, onValueChange, onSearchChange, searchDe
332
334
  sf.opcao,
333
335
  item.value === value?.value && sf.opcaoEscolhida,
334
336
  item.disabled && sf.desabilitado,
335
- ], children: [_jsx(Text, { size: "md", weight: item.value === value?.value ? 600 : 400, children: item.label }), item.description != null && (_jsx(Text, { size: "xs", tone: "muted", children: item.description }))] })) }), _jsx(RecuoDaFolha, { comTeclado: true })] })] }) }) })] }));
337
+ ], children: [_jsxs(View, { style: sf.textoDaOpcao, children: [_jsx(Text, { size: "md", weight: 500, children: item.label }), item.description != null && (_jsx(Text, { size: "xs", tone: "muted", children: item.description }))] }), item.value === value?.value && indicadorDaEscolhida(tf)] })) }), _jsx(RecuoDaFolha, { comTeclado: true })] })] }) }) })] }));
336
338
  }
337
339
  // ── A DOBRA DE ACENTO, e por que ela é sondada em vez de presumida ───────────────────────────
338
340
  // Buscar "acucar" tem de achar "açúcar" — num catálogo em português, exigir o acento certo é
package/dist/display.js CHANGED
@@ -15,7 +15,8 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
15
15
  // .badge-solid/outline :1053-4 as duas ênfases além do `soft`
16
16
  // .status aurea.css:1060 linha, gap space-2, mutedForeground, textSm
17
17
  // .status-dot aurea.css:1055 8 quadrado, redondo, halo de 3 na cor corrente a 14%
18
- // .avatar aurea.css:1196 quadrado da altura de controle, redondo, borda borderStrong
18
+ // .avatar aurea.css:1196 ~~quadrado da altura de controle~~, redondo, borda borderStrong
19
+ // (0.32.0: o lado e a letra são os do avatar nativo da referência)
19
20
  // .kpi aurea.css:1207 coluna com gap 5 — **dentro de um Card**
20
21
  //
21
22
  // ⚠ **O `.kpi` do CSS mente sozinho, e o fonte corrige:** `markup.tsx:105` mostra que o `KPI` é
@@ -73,6 +74,8 @@ const folha = criarFolha((t) => ({
73
74
  // RN, o mesmo efeito sai com opacidade no próprio anel.
74
75
  pontoDeEstado: { width: 8, height: 8, ...canto(t.size.radiusFull) },
75
76
  // ── Avatar ─────────────────────────────────────────────────────────────────────────────────
77
+ // 0.32.0 (ADR-0066): as medidas do avatar NATIVO da referência, lidas no pacote dela — 40 · 48 · 64,
78
+ // com as iniciais em 12 · 14 · 16 e peso médio. O lado sai no componente (`LADO_DO_AVATAR`).
76
79
  avatar: {
77
80
  alignItems: "center", justifyContent: "center", overflow: "hidden",
78
81
  ...canto(t.size.radiusFull), backgroundColor: t.color.surface3,
@@ -242,6 +245,14 @@ export function Status({ variant, state, children, style, ...rest }) {
242
245
  ? _jsx(Text, { size: "sm", children: rotulo })
243
246
  : rotulo] }));
244
247
  }
248
+ /**
249
+ * O lado do avatar, do pacote nativo da referência: 40 · 48 · 64 (0.32.0). Até a 0.31.0 era a altura
250
+ * de controle da WEB (`controlHSm/Md/Lg`, 32 · 36 · 40 no confortável), pequena para o polegar.
251
+ * Na escala de ESPAÇO, e não na de toque: o avatar não é controle e não anda com a densidade — é o
252
+ * que o `.avatar` da web já faz (`--space-8/10/12`). O 64 é o mesmo `space16` das molduras de 64
253
+ * da casa (a tela vazia, a miniatura do `PhotoInput`).
254
+ */
255
+ const LADO_DO_AVATAR = (t, size) => size === "sm" ? t.size.space10 : size === "lg" ? t.size.space16 : t.size.space12;
245
256
  /**
246
257
  * O retrato redondo.
247
258
  *
@@ -255,12 +266,15 @@ export function Avatar({ source, alt, fallback, size = "md", style, testID }) {
255
266
  const [falhou, setFalhou] = React.useState(false);
256
267
  // A `source` nova merece uma tentativa nova — é o `useEffect([src])` do `identity-client.tsx`.
257
268
  React.useEffect(() => { setFalhou(false); }, [source]);
258
- const lado = size === "sm" ? t.size.controlHSm : size === "lg" ? t.size.controlHLg : t.size.controlHMd;
269
+ const lado = LADO_DO_AVATAR(t, size);
259
270
  const fonte = typeof source === "string" ? { uri: source } : source;
260
271
  return (_jsx(View, { testID: testID, accessible: !!alt, accessibilityRole: alt ? "image" : undefined, accessibilityLabel: alt || undefined, style: [s.avatar, { width: lado, height: lado }, style], children: fonte && !falhou
261
272
  ? _jsx(Image, { source: fonte, style: s.imagem, onError: () => setFalhou(true) })
262
273
  : typeof fallback === "string"
263
- ? _jsx(Text, { size: size === "sm" ? "xs" : "md", weight: 700, children: fallback })
274
+ // As iniciais da referência: 12 · 14 · 16 em peso médio (eram 14 ou 16, em negrito), com
275
+ // as linhas de 16 · 20 · 24 — a mesma escada da letra do selo, e pela mesma razão: o token
276
+ // direto, porque o `size` do `Text` sobe um degrau no telefone (ADR-0050) e a referência não.
277
+ ? _jsx(Text, { weight: 500, style: LETRA_DO_SELO(t, size), children: fallback })
264
278
  : fallback }));
265
279
  }
266
280
  const TOM_DA_DIRECAO = { up: "success", down: "danger", flat: "neutral" };
@@ -0,0 +1,121 @@
1
+ import * as React from "react";
2
+ import { type StyleProp, type ViewStyle } from "react-native";
3
+ import { type AureaIcon, type AureaIconRegistry } from "./icon.js";
4
+ export type AureaSliderOrientation = "horizontal" | "vertical";
5
+ export interface SliderProps<V extends number | readonly number[] = number | readonly number[]> {
6
+ /** O rótulo. Obrigatório: é o nome das alças. `labelHidden` o tira da tela e deixa para o leitor de tela. */
7
+ label: React.ReactNode;
8
+ labelHidden?: boolean;
9
+ /** Um número, ou uma lista de dois para a faixa (duas alças). */
10
+ value?: V;
11
+ defaultValue?: V;
12
+ /** Chamado a cada passo, com o número (ou a lista, na faixa). */
13
+ onValueChange?: (value: V) => void;
14
+ /** Chamado ao soltar a alça, ao tocar no trilho e a cada passo do leitor de tela — o momento de salvar. */
15
+ onValueCommitted?: (value: V) => void;
16
+ /** Padrões da web: 0, 100 e 1. */
17
+ min?: number;
18
+ max?: number;
19
+ step?: number;
20
+ orientation?: AureaSliderOrientation;
21
+ disabled?: boolean;
22
+ /** As opções do `Intl.NumberFormat` do valor. Mesma prop da web e do `NumberField`. */
23
+ format?: Intl.NumberFormatOptions;
24
+ /** O locale do `Intl`. Sem ele, o do aparelho. */
25
+ locale?: string;
26
+ /** Mostrar o valor ao lado do rótulo. Padrão: sim. */
27
+ showValue?: boolean;
28
+ /** Com dois valores, o nome de cada alça, na ordem ("Mínimo", "Máximo"). */
29
+ thumbLabels?: readonly string[];
30
+ style?: StyleProp<ViewStyle>;
31
+ testID?: string;
32
+ }
33
+ /**
34
+ * O controle deslizante da referência: o trilho, o preenchimento e a alça em cápsula, com o rótulo
35
+ * e o valor na linha de cima.
36
+ *
37
+ * <Slider label="Volume" defaultValue={30} />
38
+ * <Slider label="Preço" value={faixa} onValueChange={setFaixa} min={0} max={1000} step={10}
39
+ * format={{style: "currency", currency: "BRL"}} locale="pt-BR" thumbLabels={["Mínimo", "Máximo"]} />
40
+ *
41
+ * Arrastar a alça move a partir de onde ela está; tocar no trilho leva a alça mais perto até ali (e
42
+ * continua arrastando, como na web). Na faixa, uma alça não passa da outra — para nela, como na
43
+ * referência. Cada alça é um elemento `adjustable`: o leitor de tela anuncia o valor formatado e
44
+ * desliza um passo para cima ou para baixo (`increment`/`decrement`).
45
+ */
46
+ export declare function Slider<V extends number | readonly number[]>({ label, labelHidden, value, defaultValue, onValueChange, onValueCommitted, min, max, step, orientation, disabled, format, locale, showValue, thumbLabels, style, testID, }: SliderProps<V>): React.JSX.Element;
47
+ export interface OTPFieldProps {
48
+ /** Quantas casas. Obrigatório, como na web: sem ele o campo não sabe quando está completo. */
49
+ length: number;
50
+ value?: string;
51
+ defaultValue?: string;
52
+ onValueChange?: (value: string) => void;
53
+ /** Esconde os dígitos atrás de um ponto. */
54
+ mask?: boolean;
55
+ disabled?: boolean;
56
+ /** O nome do campo para o leitor de tela. Dentro de um `Field`, vale o rótulo dele. */
57
+ label?: string;
58
+ style?: StyleProp<ViewStyle>;
59
+ testID?: string;
60
+ }
61
+ /**
62
+ * O campo de código de uso único — a anatomia da referência: UM campo de texto invisível por cima
63
+ * das casas, que só desenham. É ele que recebe o teclado, o colar e o código do SMS
64
+ * (`textContentType="oneTimeCode"` no iPhone, `autoComplete="sms-otp"` no Android).
65
+ *
66
+ * <Field label="Código enviado por SMS"><OTPField length={6} value={codigo} onValueChange={setCodigo} /></Field>
67
+ *
68
+ * A casa da vez ganha a borda do foco e o cursor que pisca (parado com "menos movimento"). Só
69
+ * dígitos entram, como na web. Para o leitor de tela é um campo só, com o nome do `label` (ou do
70
+ * `Field`); as casas ficam fora da árvore, para ele não ler cada dígito duas vezes.
71
+ */
72
+ export declare function OTPField({ length, value, defaultValue, onValueChange, mask, disabled, label, style, testID }: OTPFieldProps): React.JSX.Element;
73
+ export type AureaTagGroupSelectionMode = "none" | "single" | "multiple";
74
+ export type AureaTagSize = "sm" | "md" | "lg";
75
+ export type AureaTagVariant = "default" | "surface";
76
+ export interface AureaTagItem {
77
+ id: string;
78
+ label: React.ReactNode;
79
+ /** O texto do nome do X ("Remover Design"), quando o `label` não é texto. */
80
+ textValue?: string;
81
+ /** O nome de um glifo do registro, ou o próprio desenho (R-11). */
82
+ icon?: AureaIcon;
83
+ disabled?: boolean;
84
+ }
85
+ export interface TagGroupProps {
86
+ /** O nome do grupo. `labelHidden` o tira da tela e deixa para o leitor de tela. */
87
+ label: React.ReactNode;
88
+ labelHidden?: boolean;
89
+ description?: React.ReactNode;
90
+ errorMessage?: React.ReactNode;
91
+ items: readonly AureaTagItem[];
92
+ selectionMode?: AureaTagGroupSelectionMode;
93
+ selectedKeys?: readonly string[];
94
+ defaultSelectedKeys?: readonly string[];
95
+ onSelectionChange?: (keys: string[]) => void;
96
+ /** Com ele, cada etiqueta ganha o X. */
97
+ onRemove?: (ids: string[]) => void;
98
+ size?: AureaTagSize;
99
+ variant?: AureaTagVariant;
100
+ /** Registro local de ícones (o `x` e os das etiquetas), quando não há um em contexto. */
101
+ icons?: AureaIconRegistry;
102
+ style?: StyleProp<ViewStyle>;
103
+ testID?: string;
104
+ }
105
+ /**
106
+ * O grupo de etiquetas da referência: as etiquetas numa fila que quebra, com rótulo, descrição e
107
+ * erro; escolha de nenhuma, uma ou várias; e o X de tirar.
108
+ *
109
+ * <TagGroup label="Assuntos" selectionMode="multiple" items={assuntos}
110
+ * selectedKeys={escolhidos} onSelectionChange={setEscolhidos} onRemove={tirar} />
111
+ *
112
+ * ⚠ **O X é IRMÃO da etiqueta, e não filho**, ao contrário da referência (lá o botão de tirar mora
113
+ * dentro do item tocável). No iPhone, um tocável dentro de outro some para o VoiceOver (`check
114
+ * 43`); aqui a etiqueta é uma moldura com duas partes lado a lado — a que escolhe e a que tira —,
115
+ * como o `.tag-main` e o `.tag-remove` da web.
116
+ *
117
+ * Com escolha, cada etiqueta é um botão com o estado `selected`; sem escolha, é só texto. A
118
+ * escolhida ganha o fundo com 10% da marca e a letra no destaque (`primaryEmphasis`), a pintura
119
+ * da web.
120
+ */
121
+ export declare function TagGroup({ label, labelHidden, description, errorMessage, items, selectionMode, selectedKeys, defaultSelectedKeys, onSelectionChange, onRemove, size, variant, icons, style, testID, }: TagGroupProps): React.JSX.Element;