@aurea-uds/native 0.19.1 → 0.23.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/inputs.d.ts CHANGED
@@ -108,8 +108,8 @@ export interface InputProps extends Omit<TextInputProps, "style" | "editable" |
108
108
  * Normaliza o texto **quando o foco sai** — placa, documento, telefone.
109
109
  *
110
110
  * ⚠ **A Aurea entrega o MOMENTO, não o formato**, e isso é a [ADR-0024] inteira: formatar
111
- * enquanto se digita é o defeito que o USWDS publicou com reprovação WCAG registrada e que o
112
- * MUI abandonou na v6. O formato é conhecimento de DOMÍNIO — a Aurea não sabe o que é um
111
+ * enquanto se digita é o defeito que um design system público publicou com reprovação WCAG registrada e que uma
112
+ * das bibliotecas de referência abandonou. O formato é conhecimento de DOMÍNIO — a Aurea não sabe o que é um
113
113
  * documento válido em lugar nenhum, e uma tabela de formatos por país dentro de uma biblioteca
114
114
  * de interface envelhece sem ninguém perceber.
115
115
  *
@@ -156,7 +156,7 @@ interface ControleProps {
156
156
  testID?: string;
157
157
  /**
158
158
  * Onde a marca fica na altura do texto — E5, 25/09/2026. `center` (o padrão) põe a marca no meio
159
- * do bloco, como o HeroUI Native (`radio.css` e `control-field.css`: `align-items: center`).
159
+ * do bloco, como a referência (`align-items: center` no rádio e no campo de controle dela).
160
160
  * `start` a põe no meio da PRIMEIRA linha, para rótulo longo, de várias linhas.
161
161
  */
162
162
  align?: "center" | "start";
@@ -252,26 +252,38 @@ export declare function Checkbox(p: CheckboxProps): React.JSX.Element;
252
252
  * estado do app — quem sabe qual está escolhido é o `useState` da tela, e `checked` sai dele.
253
253
  */
254
254
  export declare function Radio(p: RadioProps): React.JSX.Element;
255
- /** De que lado da linha a marca fica: `end` (padrão, como o exemplo do HeroUI) ou `start`. */
255
+ /** De que lado da linha a marca fica: `end` (padrão, como o exemplo da referência) ou `start`. */
256
256
  export type AureaRadioIndicatorPlacement = "start" | "end";
257
+ /**
258
+ * O jeito dos itens (CHK-01, 06/10/2026): `list` (padrão) é a linha de sempre; `card` faz de cada
259
+ * item um CARTÃO inteiro tocável — o cartão de escolha.
260
+ */
261
+ export type AureaRadioGroupVariant = "list" | "card";
257
262
  export interface RadioGroupProps extends ViewProps {
258
263
  /** O valor do item escolhido. Controlado: quem guarda é a tela. */
259
264
  value?: string;
260
265
  onValueChange?: (value: string) => void;
261
266
  /** Desliga o grupo inteiro. */
262
267
  disabled?: boolean;
263
- /** Pinta o grupo de erro (marca e rótulo na cor de perigo), como o `isInvalid` do HeroUI. */
268
+ /** Pinta o grupo de erro (marca e rótulo na cor de perigo), como o estado inválido da referência. */
264
269
  invalid?: boolean;
265
270
  /** O nome do grupo para quem usa leitor de tela ("Forma de entrega"). */
266
271
  label?: string;
267
- /** `vertical` (padrão) ou `horizontal`, como o HeroUI. */
272
+ /** `vertical` (padrão) ou `horizontal`, como a referência. */
268
273
  orientation?: "vertical" | "horizontal";
269
274
  /**
270
- * De que lado fica a marca, em todos os itens: `end` (padrão) ou `start`. O HeroUI decide isso
275
+ * De que lado fica a marca, em todos os itens: `end` (padrão) ou `start`. A referência decide isso
271
276
  * pela ORDEM dos filhos do item (`<Radio />` antes ou depois do texto); aqui o texto é prop, então
272
- * o lado vira uma lista fechada de duas opções, com o nome que o HeroUI usa para posição.
277
+ * o lado vira uma lista fechada de duas opções, com o nome que a referência usa para posição.
273
278
  */
274
279
  indicatorPlacement?: AureaRadioIndicatorPlacement;
280
+ /**
281
+ * `card` faz de cada item um cartão de escolha (CHK-01, 06/10/2026): o cartão INTEIRO é a opção,
282
+ * com o papel de rádio para o leitor de tela. A pele é a do `Card` — o cartão comum, e o cartão
283
+ * escolhido (`variant="selected"`) no item marcado. É como a referência monta essa peça (o exemplo
284
+ * de `RadioGroup.Item` com uma superfície dentro); outra referência tem uma peça própria para isso.
285
+ */
286
+ variant?: AureaRadioGroupVariant;
275
287
  children?: React.ReactNode;
276
288
  }
277
289
  export interface RadioGroupItemProps extends Omit<PressableProps, "children" | "style" | "disabled"> {
@@ -286,19 +298,26 @@ export interface RadioGroupItemProps extends Omit<PressableProps, "children" | "
286
298
  /** O lado da marca só neste item; sem ele, vale o do grupo. */
287
299
  indicatorPlacement?: AureaRadioIndicatorPlacement;
288
300
  /**
289
- * Um ícone antes do texto (R-19, 01/10/2026). O HeroUI não tem esta prop: no `RadioGroup.Item`
290
- * dele o ícone entra como mais um filho, e a Aurea traduz filho livre em nome de ícone, como em
291
- * todo o resto. Tamanho `iconLg` (24, o mesmo da marca; o HeroUI usa 22 nos ícones de lista, que
301
+ * Um ícone antes do texto (R-19, 01/10/2026). A referência não tem esta prop: no `RadioGroup.Item`
302
+ * dela o ícone entra como mais um filho, e a Aurea traduz filho livre em nome de ícone, como em
303
+ * todo o resto. Tamanho `iconLg` (24, o mesmo da marca; a referência usa 22 nos ícones de lista, que
292
304
  * não existe aqui — escolha do Victor), vão de 12 até o texto. Na opção escolhida sai a forma
293
305
  * cheia (ADR-0053), se o registro a tiver.
294
306
  */
295
307
  icon?: AureaIcon;
308
+ /**
309
+ * Conteúdo a mais, embaixo da descrição (CHK-01) — no cartão de escolha, o preço e a lista do
310
+ * que o plano inclui. É texto para LER: o item inteiro já é o alvo do toque, e um botão aqui
311
+ * dentro sumiria para o VoiceOver (a regra do `check 43`). O leitor de tela lê o `label` e a
312
+ * `description`; o que este conteúdo diz de essencial vai no `accessibilityLabel` do item.
313
+ */
314
+ children?: React.ReactNode;
296
315
  style?: StyleProp<ViewStyle>;
297
316
  }
298
- declare function RaizDoGrupoDeRadio({ value, onValueChange, disabled, invalid, label, orientation, indicatorPlacement, style, children, ...rest }: RadioGroupProps): React.JSX.Element;
299
- declare function ItemDoGrupoDeRadio({ value, label, description, disabled, invalid, indicatorPlacement, icon, style, accessibilityLabel, ...rest }: RadioGroupItemProps): React.JSX.Element;
317
+ declare function RaizDoGrupoDeRadio({ value, onValueChange, disabled, invalid, label, orientation, indicatorPlacement, variant, style, children, ...rest }: RadioGroupProps): React.JSX.Element;
318
+ declare function ItemDoGrupoDeRadio({ value, label, description, disabled, invalid, indicatorPlacement, icon, style, accessibilityLabel, children, ...rest }: RadioGroupItemProps): React.JSX.Element;
300
319
  /**
301
- * Um conjunto de opções em que só uma fica escolhida — o `RadioGroup` do HeroUI Native.
320
+ * Um conjunto de opções em que só uma fica escolhida — o `RadioGroup` da referência.
302
321
  *
303
322
  * <RadioGroup label="Forma de entrega" value={entrega} onValueChange={setEntrega}>
304
323
  * <RadioGroup.Item value="normal" label="Normal" description="Em 5 a 7 dias úteis" />
@@ -307,7 +326,7 @@ declare function ItemDoGrupoDeRadio({ value, label, description, disabled, inval
307
326
  * </RadioGroup>
308
327
  *
309
328
  * Os filhos são livres: entre os itens cabe um `Separator`, e o grupo inteiro cabe num `Card`,
310
- * como no exemplo do HeroUI. Tocar no item já escolhido não muda nada. Com
329
+ * como no exemplo da referência. Tocar no item já escolhido não muda nada. Com
311
330
  * `indicatorPlacement="start"` a marca vai para o início da linha.
312
331
  */
313
332
  export declare const RadioGroup: typeof RaizDoGrupoDeRadio & {
@@ -328,7 +347,7 @@ export interface SwitchProps extends ControleProps {
328
347
  * O interruptor.
329
348
  *
330
349
  * ⚠ **NÃO é o `Switch` do React Native**, pela mesma razão que o `Spinner` não é o
331
- * `ActivityIndicator`: aquele desenha o interruptor do Material no Android e o do iOS no iOS —
350
+ * `ActivityIndicator`: aquele desenha o interruptor do sistema no Android e o do iOS no iOS —
332
351
  * duas aparências que o `CLAUDE.md` proíbe em voz alta. As medidas aqui são as do CSS: trilho de
333
352
  * `altura × 7/6` por `altura × 2/3`, polegar com 3 de folga.
334
353
  *
@@ -348,6 +367,12 @@ export interface SegmentedControlProps extends ViewProps {
348
367
  disabled?: boolean;
349
368
  /** Onde o controle fica quando cabe na linha: `start` (padrão), `center` ou `end` (E3). */
350
369
  justify?: AureaFilaJustify;
370
+ /**
371
+ * Ocupa a linha inteira, repartindo a sobra entre os segmentos — como na referência (A5,
372
+ * 06/10/2026). Nenhum segmento fica menor que o rótulo: quando as opções não cabem, o controle
373
+ * rola, como sem a prop. Com `fullWidth`, o `justify` não tem efeito.
374
+ */
375
+ fullWidth?: boolean;
351
376
  }
352
377
  /**
353
378
  * Um de poucos, lado a lado.
@@ -357,7 +382,7 @@ export interface SegmentedControlProps extends ViewProps {
357
382
  * há teclado aqui**, então o que resta é o que já se faz à mão: papel de grupo de rádio, um
358
383
  * `radio` por segmento, e o estado `selected` em quem está escolhido.
359
384
  */
360
- export declare function SegmentedControl({ items, value, onChange, label, disabled, justify, style, ...rest }: SegmentedControlProps): React.JSX.Element;
385
+ export declare function SegmentedControl({ items, value, onChange, label, disabled, justify, fullWidth, style, ...rest }: SegmentedControlProps): React.JSX.Element;
361
386
  export interface SelectProps {
362
387
  items: Array<{
363
388
  value: string;
package/dist/inputs.js CHANGED
@@ -39,7 +39,7 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
39
39
  // .segmented :922 linha, gap 3, padding 3, minH controlHMd, fundo muted
40
40
  import * as React from "react";
41
41
  import { Animated, KeyboardAvoidingView as KeyboardAvoidingViewRN, Modal, Platform, Pressable, ScrollView, TextInput, View, } from "react-native";
42
- import { criarFolha, estadoAcessivel, fioDoEscolhido } from "./estilos.js";
42
+ import { canto, cantosDeCima, criarFolha, estadoAcessivel, fioDoEscolhido, peleDoCartao, peleDoCartaoEscolhido } from "./estilos.js";
43
43
  import { FilaRolante } from "./rolagem.js";
44
44
  import { IconButton } from "./actions.js";
45
45
  import { Icon } from "./icon.js";
@@ -59,12 +59,12 @@ const folha = criarFolha((t) => ({
59
59
  caixa: {
60
60
  width: "100%", minWidth: 0,
61
61
  borderWidth: t.size.borderWidth, borderColor: t.color.borderStrong,
62
- borderRadius: t.size.radiusControl, backgroundColor: t.color.fieldBg,
62
+ ...canto(t.size.radiusControl), backgroundColor: t.color.fieldBg,
63
63
  color: t.color.foreground,
64
64
  },
65
65
  // O `.textarea` é o único da família com raio LG em vez do de controle: uma caixa alta em
66
66
  // pílula viraria uma cápsula, e o CSS já decidiu isso (`aurea.css:694`).
67
- areaDeTexto: { minHeight: 104, paddingVertical: t.size.space3, borderRadius: t.size.radiusLg },
67
+ areaDeTexto: { minHeight: 104, paddingVertical: t.size.space3, ...canto(t.size.radiusLg) },
68
68
  invalido: { borderColor: t.color.danger400 ?? t.color.destructive },
69
69
  desabilitado: { opacity: t.size.opacityDisabled },
70
70
  // O GRUPO é a mesma caixa do campo virada em LINHA, e os números são os mesmos: mesma borda,
@@ -73,7 +73,7 @@ const folha = criarFolha((t) => ({
73
73
  grupo: {
74
74
  flexDirection: "row", alignItems: "center", width: "100%", minWidth: 0,
75
75
  borderWidth: t.size.borderWidth, borderColor: t.color.borderStrong,
76
- borderRadius: t.size.radiusControl, backgroundColor: t.color.fieldBg,
76
+ ...canto(t.size.radiusControl), backgroundColor: t.color.fieldBg,
77
77
  gap: t.size.space1,
78
78
  },
79
79
  // Dentro do grupo o campo perde a caixa e vira só o texto — a borda passou a ser do grupo.
@@ -87,20 +87,38 @@ const folha = criarFolha((t) => ({
87
87
  borderWidth: t.size.borderWidth, borderColor: t.color.borderStrong,
88
88
  backgroundColor: t.color.fieldBg,
89
89
  },
90
- // E5: a marca no MEIO da altura do texto, como o HeroUI Native. Era `flex-start` com um
90
+ // E5: a marca no MEIO da altura do texto, como a referência. Era `flex-start` com um
91
91
  // `marginTop: 1` fixo, e a bolinha ficava presa no topo do rótulo.
92
92
  linhaDeControle: { flexDirection: "row", alignItems: "center", gap: 9 },
93
+ // CHK-01: o item do `RadioGroup variant="card"` — a pele do `Card` (`estilos.ts`), com a marca
94
+ // no alto, na linha do título.
95
+ itemCartao: { ...peleDoCartao(t), alignItems: "flex-start" },
96
+ itemCartaoEscolhido: peleDoCartaoEscolhido(t),
93
97
  linhaNoTopo: { alignItems: "flex-start" },
94
- trilho: { borderRadius: t.size.radiusFull, backgroundColor: t.color.muted,
98
+ trilho: { ...canto(t.size.radiusFull), backgroundColor: t.color.muted,
95
99
  borderWidth: t.size.borderWidth, borderColor: t.color.border, justifyContent: "center" },
96
- polegar: { position: "absolute", left: 3, borderRadius: t.size.radiusFull },
100
+ polegar: { position: "absolute", left: 3, ...canto(t.size.radiusFull) },
97
101
  segmentada: {
98
102
  flexDirection: "row", alignSelf: "flex-start", gap: 3, padding: 3,
99
- minHeight: t.size.controlHMd, borderRadius: t.size.radiusControl,
103
+ minHeight: t.size.controlHMd, ...canto(t.size.radiusControl),
100
104
  backgroundColor: t.color.muted, borderWidth: t.size.borderWidth, borderColor: "transparent",
101
105
  },
102
- segmento: { flex: 1, alignItems: "center", justifyContent: "center",
103
- paddingHorizontal: 14, borderRadius: t.size.radiusControl },
106
+ // 🔴 A5 (06/10/2026): ERA `flex: 1`, E NO NAVEGADOR ISSO QUEBRAVA O RÓTULO EM DUAS LINHAS.
107
+ // O `react-native-web` passa `flex: 1` cru para o CSS, que é `1 1 0%`: a largura do texto é
108
+ // repartida em partes IGUAIS, e o rótulo maior não cabe na parte dele ("3.000 km" com 77). No
109
+ // aparelho não quebrava — medido no Yoga oficial, com e sem `flex: 1`, o segmento fica do
110
+ // tamanho do texto —, mas lá o `flex` também não fazia nada: a cápsula ficava com 245 de 343.
111
+ // Agora o segmento é do tamanho do rótulo, como a aba do `Tabs` e o `.segmented button` da web.
112
+ // Ocupar a linha é pedido explícito: `fullWidth`, abaixo.
113
+ segmento: { alignItems: "center", justifyContent: "center",
114
+ paddingHorizontal: 14, ...canto(t.size.radiusControl) },
115
+ // `fullWidth`: o segmento CRESCE a partir do rótulo (base `auto`) e nunca encolhe abaixo dele.
116
+ // É como o grupo de botões alternáveis da referência ocupa a linha, com uma diferença declarada: lá é
117
+ // `flex: 1`, e o CSS segura o mínimo do texto sozinho (`min-width: auto`). O Yoga não tem esse
118
+ // mínimo, e base zero repartiria igual de novo — o defeito acima. Com base `auto`, os segmentos
119
+ // ficam de larguras parecidas, não iguais. Simulado no Yoga: 343 de 343, sem quebrar.
120
+ segmentoCheio: { flexGrow: 1, flexShrink: 0, flexBasis: "auto" },
121
+ segmentadaCheia: { flexGrow: 1 },
104
122
  segmentoAtivo: { backgroundColor: t.color.secondary },
105
123
  // 🔴 O FIO AMARELO DO ESCOLHIDO — ele faltava, e a falta era metade do defeito C4. Os números
106
124
  // e a razão moram no `fioDoEscolhido` (`estilos.ts`), que a aba do `Tabs` secundário também usa.
@@ -112,7 +130,8 @@ const folha = criarFolha((t) => ({
112
130
  fundoDeToque: { position: "absolute", top: 0, right: 0, bottom: 0, left: 0 },
113
131
  lista: {
114
132
  maxHeight: "60%", paddingVertical: t.size.space2,
115
- borderTopLeftRadius: t.size.radiusCard, borderTopRightRadius: t.size.radiusCard,
133
+ // HER-03 (ADR-0058): a folha que sobe de baixo tem 32, como a `BottomSheet`.
134
+ ...cantosDeCima(t.size.radiusSheet),
116
135
  backgroundColor: t.color.popover, borderWidth: t.size.borderWidth, borderColor: t.color.border,
117
136
  },
118
137
  opcao: { minHeight: t.size.controlHLg, justifyContent: "center",
@@ -346,12 +365,12 @@ function ControleMarcado({ papel, label, description, checked, onChange, disable
346
365
  accessibilityLabel: accessibilityLabel ?? (typeof label === "string" ? label : campo?.label), accessibilityHint: typeof description === "string" ? description : campo?.hint, style: [s.linhaDeControle, align === "start" && s.linhaNoTopo, inativo && s.desabilitado, style], children: [_jsx(View, { style: [
347
366
  s.marcaBase, noTopo,
348
367
  { width: lado, height: lado,
349
- borderRadius: papel === "radio" ? t.size.radiusFull : 5 },
368
+ ...canto(papel === "radio" ? t.size.radiusFull : 5) },
350
369
  checked && { backgroundColor: t.color.controlSelected, borderColor: t.color.controlSelected },
351
370
  ], children: checked && (papel === "radio"
352
371
  // O ponto do rádio é 8×8 e da cor SELECIONADA, não da cor de primeiro plano — no CSS
353
372
  // ele é `background: var(--control-selected)` sobre a marca já pintada.
354
- ? _jsx(View, { style: { width: 8, height: 8, borderRadius: t.size.radiusFull,
373
+ ? _jsx(View, { style: { width: 8, height: 8, ...canto(t.size.radiusFull),
355
374
  backgroundColor: t.color.controlSelectedForeground } })
356
375
  // O visto da web é desenhado com DUAS BORDAS de um retângulo 8×4 girado −45°. Isso
357
376
  // atravessa inteiro: `transform: rotate` e `borderLeftWidth`/`borderBottomWidth`
@@ -375,12 +394,12 @@ export function Checkbox(p) { return _jsx(ControleMarcado, { ...p, papel: "check
375
394
  */
376
395
  export function Radio(p) { return _jsx(ControleMarcado, { ...p, papel: "radio" }); }
377
396
  const GrupoDeRadio = React.createContext(null);
378
- function RaizDoGrupoDeRadio({ value, onValueChange, disabled, invalid, label, orientation = "vertical", indicatorPlacement, style, children, ...rest }) {
397
+ function RaizDoGrupoDeRadio({ value, onValueChange, disabled, invalid, label, orientation = "vertical", indicatorPlacement, variant = "list", style, children, ...rest }) {
379
398
  const t = useAureaTokens();
380
- const contexto = React.useMemo(() => ({ value, onValueChange, disabled, invalid, indicatorPlacement }), [value, onValueChange, disabled, invalid, indicatorPlacement]);
399
+ const contexto = React.useMemo(() => ({ value, onValueChange, disabled, invalid, indicatorPlacement, variant }), [value, onValueChange, disabled, invalid, indicatorPlacement, variant]);
381
400
  return (_jsx(GrupoDeRadio.Provider, { value: contexto, children: _jsx(View, { accessibilityRole: "radiogroup", accessibilityLabel: label, ...estadoAcessivel({ disabled: !!disabled }), style: [{ gap: t.size.space3, flexDirection: orientation === "horizontal" ? "row" : "column" }, style], ...rest, children: children }) }));
382
401
  }
383
- function ItemDoGrupoDeRadio({ value, label, description, disabled, invalid, indicatorPlacement, icon, style, accessibilityLabel, ...rest }) {
402
+ function ItemDoGrupoDeRadio({ value, label, description, disabled, invalid, indicatorPlacement, icon, style, accessibilityLabel, children, ...rest }) {
384
403
  const t = useAureaTokens();
385
404
  const s = folha(t);
386
405
  const grupo = React.useContext(GrupoDeRadio);
@@ -393,16 +412,17 @@ function ItemDoGrupoDeRadio({ value, label, description, disabled, invalid, indi
393
412
  const marca = t.size.space6;
394
413
  const ponto = t.size.space2 + t.size.space05;
395
414
  const noInicio = (indicatorPlacement ?? grupo?.indicatorPlacement ?? "end") === "start";
396
- const texto = (label != null || description != null) && (_jsxs(View, { style: { flex: 1, minWidth: 0 }, children: [typeof label === "string"
415
+ const cartao = grupo?.variant === "card";
416
+ const texto = (label != null || description != null || children != null) && (_jsxs(View, { style: { flex: 1, minWidth: 0 }, children: [typeof label === "string"
397
417
  ? _jsx(Text, { size: "base", weight: 500, tone: erro ? "danger" : "default", children: label }) : label, description != null && (typeof description === "string"
398
- ? _jsx(Text, { size: "xs", tone: "muted", children: description }) : description)] }, "texto"));
418
+ ? _jsx(Text, { size: "xs", tone: "muted", children: description }) : description), children != null && _jsx(View, { style: { marginTop: t.size.space3 }, children: children })] }, "texto"));
399
419
  const marcaDesenhada = (_jsx(View, { style: [
400
420
  s.marcaBase,
401
- { width: marca, height: marca, borderRadius: t.size.radiusFull },
421
+ { width: marca, height: marca, ...canto(t.size.radiusFull) },
402
422
  erro && { borderColor: perigo, backgroundColor: "transparent" },
403
423
  escolhido && { backgroundColor: erro ? perigo : t.color.controlSelected,
404
424
  borderColor: erro ? perigo : t.color.controlSelected },
405
- ], children: escolhido && _jsx(View, { style: { width: ponto, height: ponto, borderRadius: t.size.radiusFull,
425
+ ], children: escolhido && _jsx(View, { style: { width: ponto, height: ponto, ...canto(t.size.radiusFull),
406
426
  backgroundColor: t.color.controlSelectedForeground } }) }, "marca"));
407
427
  // O ícone é decorativo: o rótulo já diz o que a opção é, e o `Icon` sem `label` sai da árvore
408
428
  // de acessibilidade. A cor acompanha a do rótulo, inclusive no erro.
@@ -410,11 +430,18 @@ function ItemDoGrupoDeRadio({ value, label, description, disabled, invalid, indi
410
430
  return (_jsx(Pressable, { onPress: inativo || escolhido ? undefined : () => grupo?.onValueChange?.(value), disabled: inativo, accessibilityRole: "radio", ...estadoAcessivel({ checked: escolhido, disabled: !!inativo }), accessibilityLabel: accessibilityLabel ?? (typeof label === "string" ? label : undefined), accessibilityHint: typeof description === "string" ? description : undefined, style: [
411
431
  { flexDirection: "row", alignItems: "center", justifyContent: "space-between",
412
432
  gap: t.size.space3, minHeight: t.size.targetMin },
433
+ // CHK-01: no cartão a marca sobe para a altura do título (o cartão pode ser alto — preço,
434
+ // lista —, e uma marca no meio dele não diz de quem é). A pele é a do `Card`, e ela existe
435
+ // DESDE A MONTAGEM, com fundo e borda: escolher só troca as cores. É a regra que a `0.19.1`
436
+ // pagou no Android — caixa que ganha a pintura depois de montada perde o raio.
437
+ cartao && s.itemCartao,
438
+ cartao && escolhido && s.itemCartaoEscolhido,
439
+ cartao && erro && { borderColor: perigo },
413
440
  inativo && s.desabilitado, style,
414
441
  ], ...rest, children: noInicio ? [marcaDesenhada, glifo, texto] : [glifo, texto, marcaDesenhada] }));
415
442
  }
416
443
  /**
417
- * Um conjunto de opções em que só uma fica escolhida — o `RadioGroup` do HeroUI Native.
444
+ * Um conjunto de opções em que só uma fica escolhida — o `RadioGroup` da referência.
418
445
  *
419
446
  * <RadioGroup label="Forma de entrega" value={entrega} onValueChange={setEntrega}>
420
447
  * <RadioGroup.Item value="normal" label="Normal" description="Em 5 a 7 dias úteis" />
@@ -423,7 +450,7 @@ function ItemDoGrupoDeRadio({ value, label, description, disabled, invalid, indi
423
450
  * </RadioGroup>
424
451
  *
425
452
  * Os filhos são livres: entre os itens cabe um `Separator`, e o grupo inteiro cabe num `Card`,
426
- * como no exemplo do HeroUI. Tocar no item já escolhido não muda nada. Com
453
+ * como no exemplo da referência. Tocar no item já escolhido não muda nada. Com
427
454
  * `indicatorPlacement="start"` a marca vai para o início da linha.
428
455
  */
429
456
  export const RadioGroup = Object.assign(RaizDoGrupoDeRadio, { Item: ItemDoGrupoDeRadio });
@@ -431,7 +458,7 @@ export const RadioGroup = Object.assign(RaizDoGrupoDeRadio, { Item: ItemDoGrupoD
431
458
  * O interruptor.
432
459
  *
433
460
  * ⚠ **NÃO é o `Switch` do React Native**, pela mesma razão que o `Spinner` não é o
434
- * `ActivityIndicator`: aquele desenha o interruptor do Material no Android e o do iOS no iOS —
461
+ * `ActivityIndicator`: aquele desenha o interruptor do sistema no Android e o do iOS no iOS —
435
462
  * duas aparências que o `CLAUDE.md` proíbe em voz alta. As medidas aqui são as do CSS: trilho de
436
463
  * `altura × 7/6` por `altura × 2/3`, polegar com 3 de folga.
437
464
  *
@@ -505,14 +532,14 @@ export function Switch({ label, description, checked, onChange, disabled, size,
505
532
  * há teclado aqui**, então o que resta é o que já se faz à mão: papel de grupo de rádio, um
506
533
  * `radio` por segmento, e o estado `selected` em quem está escolhido.
507
534
  */
508
- export function SegmentedControl({ items, value, onChange, label, disabled, justify, style, ...rest }) {
535
+ export function SegmentedControl({ items, value, onChange, label, disabled, justify, fullWidth, style, ...rest }) {
509
536
  const t = useAureaTokens();
510
537
  const s = folha(t);
511
538
  const campo = useCampo();
512
539
  const inativo = disabled ?? campo?.disabled;
513
- return (_jsx(FilaRolante, { justify: justify, children: _jsx(View, { accessibilityRole: "radiogroup", accessibilityLabel: label ?? campo?.label, style: [s.segmentada, inativo && s.desabilitado, style], ...rest, children: items.map((it) => {
540
+ return (_jsx(FilaRolante, { justify: justify, fill: fullWidth, children: _jsx(View, { accessibilityRole: "radiogroup", accessibilityLabel: label ?? campo?.label, style: [s.segmentada, fullWidth && s.segmentadaCheia, inativo && s.desabilitado, style], ...rest, children: items.map((it) => {
514
541
  const ativo = it.value === value;
515
- return (_jsxs(Pressable, { onPress: inativo ? undefined : () => onChange?.(it.value), disabled: inativo, accessibilityRole: "radio", ...estadoAcessivel({ checked: ativo, disabled: !!inativo }), accessibilityLabel: typeof it.label === "string" ? it.label : undefined, style: [s.segmento, ativo && s.segmentoAtivo], children: [typeof it.label === "string"
542
+ return (_jsxs(Pressable, { onPress: inativo ? undefined : () => onChange?.(it.value), disabled: inativo, accessibilityRole: "radio", ...estadoAcessivel({ checked: ativo, disabled: !!inativo }), accessibilityLabel: typeof it.label === "string" ? it.label : undefined, style: [s.segmento, fullWidth && s.segmentoCheio, ativo && s.segmentoAtivo], children: [typeof it.label === "string"
516
543
  ? _jsx(Text, { size: "sm", weight: ativo ? 600 : 400,
517
544
  // 🔴 ERA `foreground`, E ISSO QUEBRAVA A LINGUAGEM DE SELECIONADO.
518
545
  // Achado pelo consumidor em 17/09/2026. A web pinta o escolhido com a
package/dist/layout.d.ts CHANGED
@@ -6,7 +6,7 @@ export interface StackProps extends ViewProps {
6
6
  children?: React.ReactNode;
7
7
  /**
8
8
  * Eixo cruzado (o horizontal). Padrão `stretch`: os filhos ocupam a largura, como sempre e como
9
- * na web. E2, 25/09/2026: desde que o `Button` passou a obedecer o pai (como no HeroUI Native e
9
+ * na web. E2, 25/09/2026: desde que o `Button` passou a obedecer o pai (como na referência e
10
10
  * na web), é aqui que se diz "botão do tamanho do texto" (`start`) ou "no meio" (`center`).
11
11
  */
12
12
  align?: AureaStackAlign;
@@ -158,8 +158,8 @@ export type CardProps = CardDaMarca | CardComum | CardTocavel;
158
158
  * nem link. O cartão vira UM elemento para o leitor de tela, e no iPhone quem é elemento **não
159
159
  * expõe os filhos**: o botão de dentro existiria na tela e não existiria para o VoiceOver. É o
160
160
  * defeito que o `check 43` guarda no nosso código, e aqui ele não alcança, porque o conteúdo é
161
- * do app. ⚠ **O exemplo do HeroUI faz exatamente isso** (`PressableFeedback` em volta de um
162
- * `Card` com `Button` dentro, `heroui-native@1.0.10`) — é o ponto em que a referência não serve.
161
+ * do app. ⚠ **O exemplo da biblioteca de referência faz exatamente isso** (uma superfície tocável em
162
+ * volta de um `Card` com `Button` dentro) — é o ponto em que a referência não serve.
163
163
  * Cartão com duas ações não tem `onPress`: cada ação é um botão dentro de um cartão comum.
164
164
  */
165
165
  export declare function Card({ variant, style, ...rest }: CardProps): React.JSX.Element;
package/dist/layout.js CHANGED
@@ -17,7 +17,7 @@ import { Pressable, StyleSheet, View } from "react-native";
17
17
  // `@aurea-uds/react` ja' faz (o `Stack` da web e' `HTMLAttributes & RefAttributes`, sem
18
18
  // envelope). O `ViewProps`/`TextProps` do RN 0.87 ja' declaram `ref`, entao ele viaja no
19
19
  // `...rest` sem nada a mais.
20
- import { criarFolha, REACAO_AO_TOQUE, estadoAcessivel } from "./estilos.js";
20
+ import { criarFolha, peleDoCartao, peleDoCartaoEscolhido, REACAO_AO_TOQUE, estadoAcessivel } from "./estilos.js";
21
21
  import { Text } from "./text.js";
22
22
  import { useAureaTokens, SobreAMarca } from "./theme.js";
23
23
  const folha = criarFolha((t) => ({
@@ -39,19 +39,14 @@ const folha = criarFolha((t) => ({
39
39
  // O raio 22 e a sombra vêm de token, e os dois são identidade declarada INTOCÁVEL no CLAUDE.md.
40
40
  // `boxShadow` é do RN 0.76+, e a Etapa 2 mediu que ele é 1:1 com o CSS — inclusive `spread`,
41
41
  // que o par `shadowRadius`/`elevation` perdia. Provado em aparelho no smoke do Lote 0.
42
- cardBase: {
43
- borderRadius: t.size.radiusCard,
44
- padding: t.size.cardPad,
45
- backgroundColor: t.color.card,
46
- borderWidth: t.size.borderWidth,
47
- borderColor: t.color.border,
48
- },
42
+ // A pele mora em `estilos.ts` desde 06/10/2026: o cartão de escolha (CHK-01) usa a mesma.
43
+ cardBase: peleDoCartao(t),
49
44
  card_base: {},
50
45
  card_raised: { boxShadow: [t.shadow.shadowLg] },
51
46
  card_interactive: { borderColor: t.color.borderStrong },
52
47
  // `inset` recessa em vez de flutuar — é o painel DENTRO de um card, e a ficha da web diz isso.
53
48
  card_inset: { backgroundColor: t.color.surfaceInset, borderColor: "transparent" },
54
- card_selected: { borderColor: t.color.primaryOutline, backgroundColor: t.color.surface2 },
49
+ card_selected: peleDoCartaoEscolhido(t),
55
50
  card_danger: { borderColor: t.color.danger400 ?? t.color.destructive, backgroundColor: t.color.dangerBg },
56
51
  // O amarelo é o DE PREENCHER — `primary`, o mesmo do botão sólido. A borda some: uma superfície
57
52
  // cheia não precisa de contorno, e um contorno sobre o amarelo mede 2,43 no claro (invisível).
@@ -195,8 +190,8 @@ export function Separator({ orientation = "horizontal", label, style, ...rest })
195
190
  * nem link. O cartão vira UM elemento para o leitor de tela, e no iPhone quem é elemento **não
196
191
  * expõe os filhos**: o botão de dentro existiria na tela e não existiria para o VoiceOver. É o
197
192
  * defeito que o `check 43` guarda no nosso código, e aqui ele não alcança, porque o conteúdo é
198
- * do app. ⚠ **O exemplo do HeroUI faz exatamente isso** (`PressableFeedback` em volta de um
199
- * `Card` com `Button` dentro, `heroui-native@1.0.10`) — é o ponto em que a referência não serve.
193
+ * do app. ⚠ **O exemplo da biblioteca de referência faz exatamente isso** (uma superfície tocável em
194
+ * volta de um `Card` com `Button` dentro) — é o ponto em que a referência não serve.
200
195
  * Cartão com duas ações não tem `onPress`: cada ação é um botão dentro de um cartão comum.
201
196
  */
202
197
  export function Card({ variant, style, ...rest }) {
package/dist/midia.js CHANGED
@@ -38,7 +38,7 @@ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-run
38
38
  // e serve ao mesmo caso real: o consumidor que já usa `expo-image`.
39
39
  import * as React from "react";
40
40
  import { Image as ImageRN, Pressable, View, } from "react-native";
41
- import { criarFolha, estadoAcessivel } from "./estilos.js";
41
+ import { canto, criarFolha, estadoAcessivel } from "./estilos.js";
42
42
  import { Icon } from "./icon.js";
43
43
  import { Grid } from "./layout.js";
44
44
  import { Dialog } from "./overlays.js";
@@ -51,12 +51,12 @@ const folha = criarFolha((t) => ({
51
51
  // mais numa lista que já rola.
52
52
  imagem: {
53
53
  width: "100%",
54
- borderRadius: t.size.radiusLg,
54
+ ...canto(t.size.radiusLg),
55
55
  backgroundColor: t.color.surface3 ?? t.color.muted,
56
56
  },
57
57
  contain: { backgroundColor: "transparent" },
58
58
  quebrada: { alignItems: "center", justifyContent: "center" },
59
- ladrilho: { gap: t.size.space1, padding: t.size.space1, borderRadius: t.size.radiusLg },
59
+ ladrilho: { gap: t.size.space1, padding: t.size.space1, ...canto(t.size.radiusLg) },
60
60
  ladrilhoEscolhido: { backgroundColor: t.color.secondary },
61
61
  }));
62
62
  /** `"16/9"`, `"4:3"` ou o número que o RN quer. Devolve `undefined` quando não dá para ler. */
package/dist/moldura.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { View } from "react-native";
3
- import { acentoDoTom, comOpacidade, fundoDoTom } from "./estilos.js";
3
+ import { canto, acentoDoTom, comOpacidade, fundoDoTom } from "./estilos.js";
4
4
  import { Icon } from "./icon.js";
5
5
  import { useAureaTokens } from "./theme.js";
6
6
  export function IconeEmMoldura({ icon, moldura, glifo, tone = "neutral", sobre }) {
@@ -9,7 +9,7 @@ export function IconeEmMoldura({ icon, moldura, glifo, tone = "neutral", sobre }
9
9
  : tone === "primary" ? comOpacidade(t.color.primary, 0.1)
10
10
  : fundoDoTom(t, tone);
11
11
  const cor = tone === "neutral" ? t.color.mutedForeground : acentoDoTom(t, tone);
12
- const redondo = { width: moldura, height: moldura, borderRadius: t.size.radiusFull, flexGrow: 0, flexShrink: 0 };
12
+ const redondo = { width: moldura, height: moldura, ...canto(t.size.radiusFull), flexGrow: 0, flexShrink: 0 };
13
13
  const circulo = (_jsx(View, { style: [redondo, { alignItems: "center", justifyContent: "center", backgroundColor: fundo }], children: _jsx(Icon, { name: icon, size: glifo, color: cor }) }));
14
14
  return sobre == null ? circulo : (_jsx(View, { style: [redondo, { backgroundColor: sobre }], children: circulo }));
15
15
  }
@@ -103,21 +103,27 @@ export interface BottomNavProps extends ViewProps {
103
103
  * `useBottomNavSpace()`. Fora de um provedor os dois valem zero, e nada muda.
104
104
  */
105
105
  export declare function BottomNav({ items, current, variant, indicator, width, label, style, ...rest }: BottomNavProps): React.JSX.Element;
106
- export type AureaTopbarVariant = "floating" | "flush" | "pill";
106
+ export type AureaHeaderVariant = "floating" | "flush" | "pill";
107
107
  /** O recuo dos lados da `flush`: `bar` (20, o de sempre), `page` (16, o da `Screen`) ou `none`. */
108
- export type AureaTopbarInset = "bar" | "page" | "none";
109
- export interface TopbarProps extends ViewProps {
110
- variant?: AureaTopbarVariant;
108
+ export type AureaHeaderInset = "bar" | "page" | "none";
109
+ /** @deprecated Nome antigo do `AureaHeaderVariant` (07/10/2026). Continua valendo. */
110
+ export type AureaTopbarVariant = AureaHeaderVariant;
111
+ /** @deprecated Nome antigo do `AureaHeaderInset` (07/10/2026). Continua valendo. */
112
+ export type AureaTopbarInset = AureaHeaderInset;
113
+ export interface HeaderProps extends ViewProps {
114
+ variant?: AureaHeaderVariant;
111
115
  /**
112
116
  * Só na `flush` — R-02, 24/09/2026. Por padrão a barra recua 20 dos lados e a `Screen` recua 16,
113
117
  * então o título fica 4 para dentro do conteúdo. `page` alinha os dois; `none` serve para a
114
118
  * barra que já vai dentro de um conteúdo com recuo. As outras duas variantes são caixas com
115
119
  * margem própria e ignoram esta prop.
116
120
  */
117
- inset?: AureaTopbarInset;
121
+ inset?: AureaHeaderInset;
118
122
  brand?: React.ReactNode;
119
123
  children?: React.ReactNode;
120
124
  }
125
+ /** @deprecated Nome antigo do `HeaderProps` (07/10/2026). Continua valendo. */
126
+ export type TopbarProps = HeaderProps;
121
127
  /**
122
128
  * A barra de cima.
123
129
  *
@@ -126,7 +132,7 @@ export interface TopbarProps extends ViewProps {
126
132
  *
127
133
  * ```tsx
128
134
  * <Screen padded={false}>
129
- * <Topbar brand={<Text weight={700}>Aurea</Text>} />
135
+ * <Header brand={<Text weight={700}>Aurea</Text>} />
130
136
  * <ScrollView>…</ScrollView>
131
137
  * </Screen>
132
138
  * ```
@@ -135,7 +141,9 @@ export interface TopbarProps extends ViewProps {
135
141
  * e **não há landmarks no RN**. Um papel inventado diria uma coisa errada; a barra fica sem papel,
136
142
  * e quem nomeia a tela é o conteúdo dela.
137
143
  */
138
- export declare function Topbar({ variant, inset, brand, children, style, ...rest }: TopbarProps): React.JSX.Element;
144
+ export declare function Header({ variant, inset, brand, children, style, ...rest }: HeaderProps): React.JSX.Element;
145
+ /** @deprecated `Topbar` é o nome antigo do `Header` (07/10/2026). Continua funcionando, igual; use `Header`. */
146
+ export declare function Topbar(props: HeaderProps): React.JSX.Element;
139
147
  /** Um sinal da linha (AN-04): fixada, silenciada. O `label` não aparece; é o que o leitor de tela lê. */
140
148
  export interface AureaNavListIndicator {
141
149
  icon: AureaIcon;
@@ -216,15 +224,25 @@ export interface TabsProps extends ViewProps {
216
224
  /** Onde a fila de abas fica quando cabe: `start` (padrão), `center` ou `end` (E3). */
217
225
  justify?: AureaFilaJustify;
218
226
  /**
219
- * O jeito da fila, com os nomes do HeroUI (R-12, 01/10/2026):
227
+ * O jeito da fila, com os nomes da referência (R-12, 01/10/2026):
220
228
  * - `primary` (padrão): a cápsula, com a aba aberta num fundo `secondary`.
221
229
  * - `secondary`: sem cápsula — um fio fino embaixo da fila inteira e o **fio amarelo** da casa
222
230
  * embaixo da aba aberta, o mesmo do `SegmentedControl`.
223
231
  */
224
232
  variant?: AureaTabsVariant;
233
+ /**
234
+ * O painel (C9 e MNT-05, 06/10/2026):
235
+ * - `card` (padrão): um cartão `inset`, como sempre — o conteúdo entra o recheio do cartão.
236
+ * - `plain`: sem caixa. O conteúdo começa na mesma linha do que está fora do `Tabs`, como o
237
+ * painel da referência. É o caso de um conteúdo que já é cartão, tabela ou lista: dentro de um
238
+ * painel-cartão, vira caixa dentro de caixa.
239
+ */
240
+ panel?: AureaTabsPanel;
225
241
  }
226
- /** Os dois jeitos da fila de abas, com os nomes do `Tabs` do HeroUI Native. */
242
+ /** Os dois jeitos da fila de abas, com os nomes do `Tabs` da referência. */
227
243
  export type AureaTabsVariant = "primary" | "secondary";
244
+ /** O painel do `Tabs`: cartão (padrão) ou sem caixa. O mesmo nome na web. */
245
+ export type AureaTabsPanel = "card" | "plain";
228
246
  /**
229
247
  * As abas DENTRO da tela — trocar o painel, não trocar de página.
230
248
  *
@@ -237,7 +255,7 @@ export type AureaTabsVariant = "primary" | "secondary";
237
255
  *
238
256
  * ⚠ **A fila ROLA na horizontal, e a web não rola — divergência deliberada, com fonte.** A
239
257
  * referência máxima de desenho deste projeto tem estados de rolagem na lista de abas
240
- * (`left-scroll`, `right-scroll`, `left-right-scroll` no inventário), e num telefone de 360dp
258
+ * (rolagem à esquerda, à direita e dos dois lados, no inventário), e num telefone de 360dp
241
259
  * quatro rótulos já não cabem. Espremer todas seria a outra saída, e ela apaga o rótulo — que é
242
260
  * o defeito que o app mediu no `SegmentedControl`. **A cápsula, o respiro e o raio continuam
243
261
  * sendo os do CSS**; o que muda é o transbordo.
@@ -258,4 +276,4 @@ export type AureaTabsVariant = "primary" | "secondary";
258
276
  * largura de tela grande. Não há tablet medido neste projeto, e no nativo vale demanda antes de
259
277
  * cobertura — a mesma decisão da `Table` do Lote 6.
260
278
  */
261
- export declare function Tabs({ tabs, value, onChange, label, justify, variant, style, ...rest }: TabsProps): React.JSX.Element;
279
+ export declare function Tabs({ tabs, value, onChange, label, justify, variant, panel, style, ...rest }: TabsProps): React.JSX.Element;